/* 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 : SaveDraw.c */ /* */ /* Purpose: Save a web page as a Draw file. */ /* */ /* Author : Merlyn Kline for Customer browser; */ /* This source adapted by A.D.Hodgkinson */ /* including bits from RISC_OSLib. */ /* */ /* History: 13-Dec-97: Created. */ /***************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include "swis.h" #include "flex.h" #include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */ #include "wimp.h" #include "wimplib.h" #include "event.h" #include "svcprint.h" #include "Global.h" #include "FromROSLib.h" #include "MiscDefs.h" #include "Utils.h" #include "Browser.h" #include "ChoiceDefs.h" #include "CSIM.h" #include "Fetch.h" /* (Which itself includes URLstat.h) */ #include "FileTypes.h" #include "FontManage.h" #include "Forms.h" #include "Frames.h" #include "Images.h" #include "Object.h" #include "Printing.h" /* Only for the PrintSplitFraction definition at present */ #include "PrintStyle.h" #include "Redraw.h" #include "Reformat.h" #include "Save.h" #include "Tables.h" #include "TokenUtils.h" #include "Toolbars.h" #ifdef UNIFONT #include "Unicode/iso10646.h" #include "Unifont.h" #endif #include "SaveDraw.h" /* Local definitions */ #define DRAWMARGIN 0 /* Local statics */ static FILE * outfile = NULL; /* Static function prototypes */ static int savedraw_triangle_fill (int x1, int y1, int x2, int y2, int x3, int y3, int c); static void savedraw_font_info (HStream * t, int * number, int * size); static int savedraw_input_field (browser_data * b, HStream * t, BBox * box, int colour, int menu, int * size); static int savedraw_button (browser_data * b, HStream * t, BBox * box, int in, int * size); static int savedraw_switch (int x, int y, char * spr, int * size); static int savedraw_texthdr (browser_data * b, HStream * tp, int x, int y, int w, int h, int base, int length, int bgnd); static int savedraw_draw_placeholder (browser_data * b, WimpRedrawWindowBlock * r, BBox * holder, HStream * token, const char * text, int * size); static int savedraw_textarea_redraw (browser_data * b, HStream * token, BBox *box, BBox *gw, int fh, int multiline, int password, int * size); static int savedraw_create (browser_data * b, int bgimages, int * size); static int savedraw_create_r (int toplevel, int xorg, int yorg, browser_data * b, reformat_cell * d, int noback, WimpRedrawWindowBlock * r, int bgimages, int * size, int * tsize); /*************************************************/ /* savedraw_write_bytes() */ /* */ /* Outputs a given number of bytes of a given */ /* string to the FILE * set up in the global */ /* 'outfile', returning a success flag. */ /* */ /* Parameters: Pointer to an array of bytes; */ /* */ /* Number of characters from the */ /* array to save. */ /* */ /* Returns: 1 if successful, 0 if failed. */ /* */ /* Assumes: An appropriately opened file is */ /* accessible through the global */ /* FILE * 'outfile'. */ /*************************************************/ int savedraw_write_bytes(const char * s, unsigned int n) { _kernel_swi_regs r; if (!s || !outfile) return 0; /* Use _kernel_swi as it sets up */ /* _kernel_last_oserror. */ r.r[0] = 2; r.r[1] = outfile->__file; r.r[2] = (int) s; r.r[3] = n; if (!r.r[1] || _kernel_swi(OS_GBPB, &r, &r)) return 0; return 1; } /*************************************************/ /* savedraw_rectangle_fill() */ /* */ /* Output a filled rectangle. */ /* */ /* Parameters: x position of the item in Draw */ /* coordinates; */ /* */ /* y position of the item in Draw */ /* coordinates; */ /* */ /* Item's width in Draw coordinates; */ /* */ /* Item's height in Draw */ /* coordinates; */ /* */ /* Colour of the rectangle. */ /*************************************************/ int savedraw_rectangle_fill(int x, int y, int w, int h, int c) { draw_pathstrhdr * ph; int * i; char d[DSIZE_FRECT]; if (w <= 0 || h <= 0) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; StrNCpy0(erb.errmess, "Rectangle width or height is zero in savedraw_rectangle_fill, so no object written out - if calculating file size, the value will be incorrect."); show_error_ret(&erb); #endif return 1; } /* To ensure the lines are visible, should round x and y */ /* to a multiple of 2 OS units. */ x = x & ~(OTD(2) - 1); y = y & ~(OTD(2) - 1); /* We'll compile the structure in 'd' */ ph = (draw_pathstrhdr *) d; /* Fill in the header items */ ph->tag = draw_OBJPATH; ph->size = DSIZE_FRECT; ph->bbox.xmin = x; ph->bbox.xmax = x + w; ph->bbox.ymin = y; ph->bbox.ymax = y + h; ph->fillcolour = c; ph->pathcolour = -1; ph->pathwidth = 0; ph->pathstyle.joincapwind = 0; ph->pathstyle.reserved8 = 0; ph->pathstyle.tricapwid = 0; ph->pathstyle.tricaphei = 0; /* Now do the body */ i = (int *) (d + sizeof(draw_pathstrhdr)); *i++ = draw_PathMOVE; *i++ = x; *i++ = y; *i++ = draw_PathLINE; *i++ = x; *i++ = y + h - 1; *i++ = draw_PathLINE; *i++ = x + w - 1; *i++ = y + h - 1; *i++ = draw_PathLINE; *i++ = x + w - 1; *i++ = y; *i++ = draw_PathLINE; *i++ = x; *i++ = y; *i++ = draw_PathTERM; return savedraw_write_bytes(d, sizeof(d)); } /*************************************************/ /* savedraw_triangle_fill() */ /* */ /* Output a filled triangle. */ /* */ /* Parameters: x and y coordinates of the three */ /* vertices, in Draw coordinates; */ /* */ /* Colour of the triangle. */ /*************************************************/ static int savedraw_triangle_fill(int x1, int y1, int x2, int y2, int x3, int y3, int c) { draw_pathstrhdr * ph; int * i; char d[DSIZE_FTRIA]; /* We'll compile the structure in 'd' */ ph = (draw_pathstrhdr *) d; /* Fill in the header items */ ph->tag = draw_OBJPATH; ph->size = DSIZE_FTRIA; if (x1 < x2) ph->bbox.xmin = x1, ph->bbox.xmax = x2; else ph->bbox.xmin = x2, ph->bbox.xmax = x1; if (y1 < y2) ph->bbox.ymin = y1, ph->bbox.ymax = y2; else ph->bbox.ymin = y2, ph->bbox.ymax = y1; if (x3 < ph->bbox.xmin) ph->bbox.xmin = x3; if (y3 < ph->bbox.ymin) ph->bbox.ymin = y3; if (x3 > ph->bbox.xmax) ph->bbox.xmax = x3; if (y3 > ph->bbox.ymax) ph->bbox.ymax = y3; ph->fillcolour = c; ph->pathcolour = -1; ph->pathwidth = 0; ph->pathstyle.joincapwind = 0; ph->pathstyle.reserved8 = 0; ph->pathstyle.tricapwid = 0; ph->pathstyle.tricaphei = 0; /* Now do the body */ i = (int *) (d + sizeof(draw_pathstrhdr)); *i++ = draw_PathMOVE; *i++ = x1; *i++ = y1; *i++ = draw_PathLINE; *i++ = x2; *i++ = y2; *i++ = draw_PathLINE; *i++ = x3; *i++ = y3; *i++ = draw_PathLINE; *i++ = x1; *i++ = y1; *i++ = draw_PathTERM; return savedraw_write_bytes(d, sizeof(d)); } /*************************************************/ /* savedraw_font_info() */ /* */ /* Find details of the font a token would use. */ /* */ /* Parameters: Pointer to the HStream struct */ /* relevant to the enquiry; */ /* */ /* Pointer to an int, in which the */ /* number of the font (from the Draw */ /* file's font table) is returned; */ /* */ /* Pointer to an int, in which the */ /* font size in 16ths pt. is */ /* written. */ /* */ /* Assumes: Neither int pointer may be NULL. */ /*************************************************/ static void savedraw_font_info(HStream * t, int * number, int * size) { int f, i, b; /* Find details of the font in use */ fm_token_font_info(t, &f, size, &i, &b); /* Convert the given face, italic and bold flags into a font number */ /* for the Draw file */ *number = (f * 4 + (i ? 1 : 0) + (b ? 2 : 0) + 1); /* Finished */ return; } /*************************************************/ /* savedraw_input_field() */ /* */ /* For forms, create an input field element. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the element; */ /* */ /* Pointer to the token representing */ /* this element; */ /* */ /* BBox of the field, in window */ /* coords (and thus OS units); */ /* */ /* Border colour (as a palette */ /* entry, for more details see */ /* redraw_set_colour()); */ /* */ /* 1 if this is a display field, */ /* i.e. it has a menu icon, and the */ /* border colour is ignored, else 0; */ /* */ /* Pointer to an int if wanting to */ /* know the size of the item only, */ /* else NULL (NB the int contents */ /* are not updated - the pointer is */ /* acting like a flag, basically). */ /* */ /* Returns: Size of item written out, or 0 if */ /* it failed. */ /*************************************************/ static int savedraw_input_field(browser_data * b, HStream * t, BBox * box, int colour, int menu, int * size) { int w, h; int tsize; w = box->xmax - box->xmin; h = box->ymax - box->ymin; if (menu) { int sw; BBox shorter; shorter = *box; if (read_sprite_size("fgright", &sw, NULL)) sw = 44; shorter.xmax -= (sw + 8); if (shorter.xmax < shorter.xmin) shorter.xmax = shorter.xmin + sw; /* Redraw the display region as a slabbed button */ return savedraw_button(b, t, &shorter, 2, size); } else { /* Redraw the inside in white */ tsize = DSIZE_FRECT; if (!savedraw_rectangle_fill(OTD(box->xmin), OTD(box->ymin), OTD(w), OTD(h), Redraw_Colour_White)) return 0; /* Redraw the border */ tsize += DSIZE_FRECT * 4; if (!size) { if (!savedraw_rectangle_fill(OTD(box->xmin), OTD(box->ymin), OTD(4), OTD(h), colour)) return 0; if (!savedraw_rectangle_fill(OTD(box->xmax - 4), OTD(box->ymin), OTD(4), OTD(h), colour)) return 0; if (!savedraw_rectangle_fill(OTD(box->xmin + 4), OTD(box->ymin), OTD(w - 8), OTD(4), colour)) return 0; if (!savedraw_rectangle_fill(OTD(box->xmin + 4), OTD(box->ymax - 4), OTD(w - 8), OTD(4), colour)) return 0; } } return tsize; } /*************************************************/ /* savedraw_button() */ /* */ /* For forms, redraws a button element. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the element; */ /* */ /* Pointer to the token representing */ /* this element; */ /* */ /* BBox of the field, in window */ /* coords (and thus OS units); */ /* */ /* 1 to be slabbed in, 2 to be */ /* slabbed in but with a light grey */ /* background rather than dark, else */ /* 0; */ /* */ /* Pointer to an int if wanting to */ /* know the size of the item only, */ /* else NULL (NB the int contents */ /* are not updated - the pointer is */ /* acting like a flag, basically). */ /* */ /* Returns: Size of item written out, or 0 if */ /* it failed. */ /*************************************************/ static int savedraw_button(browser_data * b, HStream * t, BBox * box, int in, int * size) { int w, h, c; int tsize; w = box->xmax - box->xmin; h = box->ymax - box->ymin; c = (in == 1) ? Redraw_Colour_MidGrey : Redraw_Colour_BackGrey; tsize = DSIZE_FRECT; if (!size && !savedraw_rectangle_fill(OTD(box->xmin), OTD(box->ymin), OTD(w), OTD(h), c)) return 0; tsize += DSIZE_FRECT * 4; c = in ? Redraw_Colour_PlinthGrey : Redraw_Colour_AlmostWhite; if (!size) { if (!savedraw_rectangle_fill(OTD(box->xmin), OTD(box->ymin), OTD(2), OTD(h), c)) return 0; if (!savedraw_rectangle_fill(OTD(box->xmin + 2), OTD(box->ymin + 2), OTD(2), OTD(h - 2), c)) return 0; if (!savedraw_rectangle_fill(OTD(box->xmin + 4), OTD(box->ymax - 2), OTD(w - 4), OTD(2), c)) return 0; if (!savedraw_rectangle_fill(OTD(box->xmin + 4), OTD(box->ymax - 4), OTD(w - 6), OTD(2), c)) return 0; } tsize += DSIZE_FRECT * 4; c = in ? Redraw_Colour_AlmostWhite : Redraw_Colour_PlinthGrey; if (!size) { if (!savedraw_rectangle_fill(OTD(box->xmin + 2), OTD(box->ymin), OTD(w - 2), OTD(2), c)) return 0; if (!savedraw_rectangle_fill(OTD(box->xmin + 4), OTD(box->ymin + 2), OTD(w - 4), OTD(2), c)) return 0; if (!savedraw_rectangle_fill(OTD(box->xmax - 4), OTD(box->ymin + 4), OTD(2), OTD(h - 8), c)) return 0; if (!savedraw_rectangle_fill(OTD(box->xmax - 2), OTD(box->ymin + 4), OTD(2), OTD(h - 6), c)) return 0; } return tsize; } /*************************************************/ /* savedraw_switch() */ /* */ /* Output a switch (small sprite) at given */ /* coordinates. */ /* */ /* Parameters: x position of the item in Draw */ /* coordinates; */ /* */ /* y position of the item in Draw */ /* coordinates; */ /* */ /* Pointer to the sprite's name; */ /* */ /* Pointer to an int if wanting to */ /* know the size of the item only, */ /* else NULL (NB the int contents */ /* are not updated - the pointer is */ /* acting like a flag, basically). */ /*************************************************/ static int savedraw_switch(int x, int y, char * spr, int * size) { draw_spristrhdr h; _kernel_swi_regs r; sprite_header * sh; int asize = 0; int wid, hei; h.tag = draw_OBJSPRITE; /* Find the sprite. Use _kernel_swi to set up */ /* _kernel_last_oserror. */ r.r[0] = 280; r.r[1] = (int) sprite_block; r.r[2] = (int) spr; if (_kernel_swi(OS_SpriteOp, &r, &r)) return 0; else sh = (sprite_header *) r.r[2]; if (read_sprite_size(spr, &wid, &hei)) return 0; asize = sh->next + sizeof(h); h.size = (int) WordAlign(asize); x = OTD(x); y = OTD(y); h.bbox.xmin = x; h.bbox.ymin = y; h.bbox.xmax = h.bbox.xmin + OTD(wid); h.bbox.ymax = h.bbox.ymin + OTD(hei); if (!size) { if (!savedraw_write_bytes((char *) &h, sizeof(h))) return 0; if (!savedraw_write_bytes((char *) sh, asize - sizeof(h))) return 0; if (asize != h.size) { if (!savedraw_write_bytes(" ", h.size - asize)) return 0; } } return h.size; } /*************************************************/ /* savedraw_texthdr() */ /* */ /* Outputs a header for a text item. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* for which the item is to be */ /* output; */ /* */ /* Pointer to an HStream struct */ /* from which the foreground colour */ /* and font to use is drawn (and */ /* nothing else); */ /* */ /* x position of the item in Draw */ /* coordinates; */ /* */ /* y position of the item in Draw */ /* coordinates; */ /* */ /* Item's width in Draw coordinates; */ /* */ /* Item's height in Draw */ /* coordinates; */ /* */ /* Baseline offset for the text */ /* (added to the y coordinate); */ /* */ /* Length of the string that this */ /* header will be representing; */ /* */ /* Background colour to use. */ /*************************************************/ static int savedraw_texthdr(browser_data * b, HStream * tp, int x, int y, int w, int h, int base, int length, int bgnd) { draw_trfmtextstrhdr hdr; int number, size; if (w <= 0 || h <= 0) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; StrNCpy0(erb.errmess, "Width or height is zero in savedraw_texthdr, adjusting to 2 OS units to try and ensure output file is valid."); show_error_ret(&erb); #endif if (w <= 0) w = OTD(2); if (h <= 0) h = OTD(2); } /* Find info on the font */ savedraw_font_info(tp, &number, &size); /* Fill in the header */ hdr.tag = draw_OBJTEXTTRFM; hdr.size = sizeof(draw_trfmtextstrhdr) + (int) WordAlign(length + 1); hdr.bbox.xmin = x; hdr.bbox.xmax = x + w; hdr.bbox.ymin = y; hdr.bbox.ymax = y + h; hdr.matrix[0] = 0x10000; hdr.matrix[1] = 0; hdr.matrix[2] = 0; hdr.matrix[3] = 0x10000; hdr.matrix[4] = 0; hdr.matrix[5] = 0; hdr.fontflags = SaveDraw_FontFlags_Kerned; hdr.textcolour = redraw_token_colour(b, tp); hdr.background = bgnd; hdr.textstyle.fontref = fm_system_font() ? 0 : number; hdr.textstyle.reserved8 = 0; hdr.textstyle.reserved16 = 0; hdr.fsizey = size * 40; if (number <= 8) hdr.fsizex = size * 40; else hdr.fsizex = (size * 4 * choices.tt_aspect) / 10; if (fm_system_font()) hdr.fsizex /= 2; hdr.coord.x = x; hdr.coord.y = y + base; /* Exit through the save routines */ return savedraw_write_bytes((char *) &hdr, sizeof(hdr)); } /*************************************************/ /* savedraw_draw_placeholder() */ /* */ /* Redraws a slabbed in place holder (unless the */ /* item is very small, in which case just at */ /* thin black border is plotted) for a given */ /* token, with optional text inside. */ /* */ /* Parameters: A pointer to a browser_data */ /* structure relevant to the redraw; */ /* */ /* A WimpRedrawWindowBlock pointer, */ /* with window area and redraw */ /* rectangle details filled in; */ /* */ /* Pointer to a BBox in which xmin */ /* and ymin hold the screen coords */ /* for the bottom left hand corner, */ /* and xmax and ymax hold the width */ /* and height of the placeholder in */ /* OS units; */ /* */ /* Pointer to the HStream struct the */ /* placeholder is to represent; */ /* */ /* Pointer to a null-terminated */ /* piece of to plot inside, or NULL; */ /* */ /* Pointer to an int if wanting to */ /* know the size of the item only, */ /* else NULL (NB the int contents */ /* are not updated - the pointer is */ /* acting like a flag, basically). */ /* */ /* Returns: Size of item written out, or 0 if */ /* it failed. */ /*************************************************/ static int savedraw_draw_placeholder(browser_data * b, WimpRedrawWindowBlock * r, BBox * holder, HStream * token, const char * text, int * size) { int tsize = 0; BBox ph = *holder; // In case we want to adjust it later, e.g. for H/VSPACE /* A slabbed box if the size is great enough */ if (ph.xmax > 8 && ph.ymax > 8) { tsize += 4 * DSIZE_FRECT; if (!size) { if (!savedraw_rectangle_fill(OTD(ph.xmin + 2), OTD(ph.ymin + 2), OTD(ph.xmax - 4), OTD(4), Redraw_Colour_AlmostWhite)) return 0; if (!savedraw_rectangle_fill(OTD(ph.xmax + ph.xmin - 6), OTD(ph.ymin + 2), OTD(4), OTD(ph.ymax - 4), Redraw_Colour_AlmostWhite)) return 0; if (!savedraw_rectangle_fill(OTD(ph.xmin + 2), OTD(ph.ymax + ph.ymin - 6), OTD(ph.xmax - 6), OTD(4), Redraw_Colour_MidGrey)) return 0; if (!savedraw_rectangle_fill(OTD(ph.xmin + 2), OTD(ph.ymin + 4), OTD(4), OTD(ph.ymax - 6), Redraw_Colour_MidGrey)) return 0; } } /* Otherwise a thin black frame */ else { if (ph.xmax < 2) ph.xmax = 2; if (ph.ymax < 2) ph.ymax = 2; tsize += 4 * DSIZE_FRECT; if (!size) { if (!savedraw_rectangle_fill(OTD(ph.xmin), OTD(ph.ymin), OTD(ph.xmax), OTD(2), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(ph.xmin), OTD(ph.ymin), OTD(2), OTD(ph.ymax), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(ph.xmin + ph.xmax - 2), OTD(ph.ymin), OTD(2), OTD(ph.ymax), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(ph.xmin), OTD(ph.ymin + ph.ymax - 2), OTD(ph.xmax), OTD(2), Redraw_Colour_Black)) return 0; } } /* Output any text that there is */ if (text && *text) { /* Find out the bounding box needed to contain the text */ int h, xpos, vcent, stringwidth, stringheight, fsize; BBox fbox; BBox * ibox = NULL; fbox.xmin = fbox.ymin = 0; /* Claim the font */ fsize = (fm_size(token->fontsize) * 80) / 100; h = fm_find_font(b, "sans", fsize, fsize, 0, 0); /* Find the string width of the ALT text */ fm_get_string_width(h, text, Reformat_AsWideAsPossible_MP, strlen(text), -1, NULL, &stringwidth); convert_to_os(stringwidth, &stringwidth); /* Find the font height */ fm_font_box(h, &fbox); stringheight = fbox.ymax - fbox.ymin; /* Set xpos to the horizontal offset to plot at. */ /* Remember that 'box' contains the bottom */ /* left coordinates of the image, then the width */ /* and height in OS units in xmax and ymax. */ /* Similarly, centre vertically */ vcent = (ph.ymax - stringheight) / 2 - fbox.ymin; if (vcent <= 0) vcent = 10; vcent += ph.ymin; xpos = (ph.xmax - stringwidth) / 2; if (xpos <= 0) xpos = 10; xpos += ph.xmin; /* Now work out the bounding BBox from the placeholder BBox, */ /* allowing for a small edge gap before the text gets dropped */ /* out completely (we can't clip in a Draw file, so must just */ /* not output the text if it won't fit). */ fbox.xmin = ph.xmin + 8; fbox.ymin = ph.ymin;// + 8; fbox.xmax = ph.xmin + ph.xmax - 8; /* Since ph.xmax = placeholder width) */ fbox.ymax = ph.ymin + ph.ymax;// - 8; /* Since ph.ymax = placeholder height) */ /* Can only proceed if there's a > 0 size bounding box */ if (fbox.xmin < fbox.xmax && fbox.ymin < fbox.ymax) { /* Find the intersection of the area of the image in which */ /* we can plot ALT text and the 'redraw' rectangle */ ibox = intersection(&fbox, &r->redraw_area); /* If this box is smaller than that required for the ALT text, */ /* don't proceed */ if ( ibox && ibox->xmax - ibox->xmin >= stringwidth && ibox->ymax - ibox->ymin >= stringheight ) { draw_trfmtextstrhdr hdr; int c = redraw_token_colour(b, token); int length = strlen(text) + 1; char null = '\0'; /* Construct the header */ hdr.tag = draw_OBJTEXTTRFM; hdr.size = sizeof(draw_trfmtextstrhdr) + (int) WordAlign(length); hdr.bbox.xmin = OTD(xpos); hdr.bbox.xmax = OTD(xpos + stringwidth); hdr.bbox.ymin = OTD(vcent); hdr.bbox.ymax = OTD(vcent + stringheight); hdr.matrix[0] = 0x10000; hdr.matrix[1] = 0; hdr.matrix[2] = 0; hdr.matrix[3] = 0x10000; hdr.matrix[4] = 0; hdr.matrix[5] = 0; hdr.fontflags = SaveDraw_FontFlags_Kerned; hdr.textcolour = c; hdr.background = redraw_background_colour(b, c); hdr.textstyle.fontref = fm_system_font() ? 0 : 5; hdr.textstyle.reserved8 = 0; hdr.textstyle.reserved16 = 0; hdr.fsizex = fsize * 40; hdr.fsizey = fsize * 40; if (fm_system_font()) hdr.fsizex /= 2; hdr.coord.x = OTD(xpos); hdr.coord.y = OTD(vcent); tsize += sizeof(hdr) + (int) WordAlign(length); if (!size) { if (!savedraw_write_bytes((char *) &hdr, sizeof(hdr))) return 0; /* Write the text itself */ if (!savedraw_write_bytes(text, length - 1)) return 0; if (!savedraw_write_bytes(&null, 1)) return 0; if (length != (int) WordAlign(length)) { if (!savedraw_write_bytes(" ", (int) WordAlign(length) - length)) return 0; } } /* Underline text if it's a link and the browser is set to underline links */ if (b->underline_links && ISLINK(token)) { tsize += DSIZE_FRECT; if (!size && !savedraw_rectangle_fill(OTD(xpos), OTD(vcent - 7), OTD(stringwidth), OTD(2), c)) return 0; } } } } return tsize; } /*************************************************/ /* savedraw_textarea_redraw() */ /* */ /* Outputs the text in a text area object. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the text area; */ /* */ /* Pointer to the token representing */ /* the text area; */ /* */ /* Pointer to a BBox holding the */ /* bounding box of the text area, in */ /* screen coords (so OS units); */ /* */ /* Pointer to a BBox holding the */ /* current graphics window (usually */ /* from a call to the Wimp to redraw */ /* something - remember that this */ /* can't be read during a printing */ /* routine as VDU variables may not */ /* be read during printing); */ /* */ /* A font handle for the font to use */ /* for the redraw; */ /* */ /* 1 if the text area has multiple */ /* lines, 0 to render all text on */ /* one line only; */ /* */ /* 1 if the text area represents a */ /* password object (show the text as */ /* a line of *s, instead of the */ /* actual chars), else 0; */ /* */ /* Pointer to an int if wanting to */ /* know the size of the item only, */ /* else NULL (NB the int contents */ /* are not updated - the pointer is */ /* acting like a flag, basically). */ /* */ /* Returns: Size of item written out (which */ /* may be zero), or -1 for failure - */ /* note how this differs from most */ /* other functions in this source! */ /*************************************************/ static int savedraw_textarea_redraw(browser_data * b, HStream * token, BBox *box, BBox *gw, int fh, int multiline, int password, int * size) { int nxmin, nymin, nxmax, nymax; int lh, lb, length, used; int xs, ys, y; int tsize = 0; BBox area, fontbox; char c; char * p, * t; /* Set 'area' to hold the bounding box of the text area */ area = *box; /* Bring the coordinates in to account for the text area's border */ area.xmin += 8; area.ymin += 8; area.xmax -= 8; area.ymax -= 8; /* Get nxmin, nxmax, nymin anx nymax to hold the coordinates of */ /* a BBox formed by the intersection of the text area and the */ /* current graphics rectangle. */ nxmin = area.xmin > gw->xmin ? area.xmin : gw->xmin; nxmax = area.xmax < gw->xmax ? area.xmax : gw->xmax; nymin = area.ymin > gw->ymin ? area.ymin : gw->ymin; nymax = area.ymax < gw->ymax ? area.ymax : gw->ymax; nxmin &= ~1; nymin &= ~1; nxmax &= ~1; nymax &= ~1; /* If the minimum coords are less than / equal to the maximums, */ /* there is nothing to redraw. */ if (nxmin >= nxmax || nymin >= nymax) return 0; /* Get the height of an individual line, based on the height of */ /* the font to be used plus vertical spacing considerations. */ fm_font_box(fh, &fontbox); form_get_linesize(&fontbox, &lh, &lb); /* Get the text for the text area */ p = form_get_field_text(b, token); /* Only proceed if there's text to draw */ if (p) { /* Assume a zero scroll offset (so the output */ /* Draw file reflects a 'dead' unedited page, */ /* in the same way that the keyboard selected */ /* item, if present, isn't shown highlighted) */ xs = ys = 0; y = area.ymax + ys; do { /* The text may be terminated by newlines or null bytes; if */ /* a newline, this implies a split point for a text area. */ t = strchr(p, '\n'); if (!t) t = strchr(p, 0); /* Ensure for C's purposes (sigh) that there is a zero terminator */ /* at the end of the chunk to plot. */ c = *t; *t = 0; length = used = strlen(p); /* If the line is in the visible region, plot it. Note the use of */ /* the FE_PassCode line of asterisks - if a password is being */ /* entered, as many of these as there are characters in the */ /* real string will be displayed instead of that string. */ if (length && y - lh < area.ymax) { int tw; char null = '\0'; length ++; /* (Initial condition) */ do { length --; if (fm_get_string_width(fh, password ? FE_PassCode : p, 0x1000000, length, -1, NULL, &tw)) return -1; convert_to_os(tw, &tw); } while (length && tw > nxmax - nxmin); if (length) { /* Output the header */ tsize += sizeof(draw_trfmtextstrhdr); if (!savedraw_texthdr(b, token, OTD(area.xmin - xs), OTD(y - lh + lb), OTD(tw), OTD(lh), 0, length, (token->tagno == TAG_SELECT) ? Redraw_Colour_BackGrey : Redraw_Colour_White)) return -1; /* The count should include a terminator at this point */ length++; /* Output the text ifself */ tsize += (int) WordAlign(length); if (!savedraw_write_bytes(password ? FE_PassCode : p, length - 1)) return -1; if (!savedraw_write_bytes(&null, 1)) return -1; if (length != (int) WordAlign(length)) { if (!savedraw_write_bytes(" ", (int) WordAlign(length) - length)) return -1; } } } y -= lh; /* Restore the character that was altered to a null byte earlier */ *t = c; /* Advance the string pointer past the text just plotted */ p += used; /* If we're on a newline, there must be another chunk of string to */ /* plot, so move the pointer past it. */ if (*p == '\n') p++; /* Keep looping whilst in the visible area and there's data left to */ /* plot, provided that this was flagged as a multiline object redraw. */ } while (y>area.ymin && multiline && *p); } return tsize; } /*************************************************/ /* savedraw_create() */ /* */ /* Output a Draw file representing the contents */ /* of a given browser, or find out how big the */ /* file would be. The static 'outfile' local to */ /* this file should hold a valid FILE * to */ /* output to in the first case. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* to represent in the Draw file; */ /* */ /* 1 to include any background */ /* image present on the browser's */ /* page, else 0; */ /* */ /* Pointer to an int to take the */ /* file size if that's all you want, */ /* or NULL to actually output the */ /* file. */ /*************************************************/ static int savedraw_create(browser_data * b, int bgimages, int * size) { WimpRedrawWindowBlock r; _kernel_oserror * e; HStream * selected; int success; int tsize = 40; /* Draw file header */ int height; /* Compile a pseudo-redraw block for the Draw file, */ /* in screen coordinates. */ height = reformat_return_extent(b, b->cell); if (height < b->display_height) height = b->display_height; r.visible_area.xmin = r.redraw_area.xmin = 0; r.visible_area.ymin = r.redraw_area.ymin = 0; r.visible_area.xmax = r.redraw_area.xmax = b->display_extent; r.visible_area.ymax = r.redraw_area.ymax = height; r.window_handle = b->window_handle; r.xscroll = 0; r.yscroll = 0; /* If required, write the Draw file header */ tsize += DSIZE_FRECT; /* Background */ if (size) *size = 0; else { draw_fileheader h; /* Draw file ID */ strncpy(h.title, "Draw", sizeof(h.title)); /* Draw file version */ h.majorstamp = 201; h.minorstamp = 0; /* Program name - we do not rely on a field immediately */ /* after 'progident' to take a string terminator here. */ strncpy(h.progident, program_name, sizeof(h.progident)); if (strlen(program_name) < sizeof(h.progident)) { int i = strlen(program_name); int j = sizeof(h.progident) - i; while (j) h.progident[i++] = ' ', j--; } /* Whole file's bounding box */ h.bbox.xmin = DRAWMARGIN + OTD(r.redraw_area.xmin); h.bbox.ymin = DRAWMARGIN + OTD(r.redraw_area.ymin); h.bbox.xmax = DRAWMARGIN * 2 + OTD(r.redraw_area.xmax); h.bbox.ymax = DRAWMARGIN * 2 + OTD(r.redraw_area.ymax); if (!savedraw_write_bytes((char *) &h, sizeof(h))) return 0; } /* Unless we're in system font, list the fonts used */ if (!fm_system_font()) { int f, i, b, n; char fontname[Limits_FontName]; int fsize; fsize = 8; for (f = 0; f < 12; f++) /* 12 faces */ { n = f / 4; /* Internal font number */ i = (f % 4) & 1; /* Italic flag, lsb (bit 0) */ b = (f % 4) >> 1; /* Bold flag, bit 1 */ e = fm_write_name(n, fontname, i, b); if (e) show_error_ret(e); fsize += strlen(fontname) + 2; } tsize += (int) WordAlign(fsize); /* If required, write the header out */ if (!size) { *(int*) fontname = draw_OBJFONTLIST; *(int*) (fontname + 4) = (int) WordAlign(fsize); if (!savedraw_write_bytes(fontname, 8)) return 0; for (f = 0; f < 12; f++) /* 12 faces */ { n = f / 4; /* Internal font number */ i = (f % 4) & 1; /* Italic flag, lsb (bit 0) */ b = (f % 4) >> 1; /* Bold flag, bit 1 */ e = fm_write_name(n, fontname + 1, i, b); if (e) show_error_ret(e); fontname[0] = f + 1; if (!savedraw_write_bytes(fontname, strlen(fontname + 1) + 2)) return 0; } /* Word align the file contents */ if (fsize != (int) WordAlign(fsize)) { if (!savedraw_write_bytes(" ", (int) WordAlign(fsize) - fsize)) return 0; } } } /* Now draw the main page (recursively, for tables); */ /* note that 'b->selected' is cleared to avoid */ /* highlighted tokens coming out as SeleColour in */ /* the draw output. */ selected = b->selected; b->selected = NULL; success = savedraw_create_r(1, 0, 0, b, b->cell, 0, &r, bgimages, size, &tsize); b->selected = selected; /* Finished */ return success; } /*************************************************/ /* savedraw_create_r() */ /* */ /* Recursive back-end to savedraw_create. API is */ /* similar in many ways to redraw_draw_r. */ /* */ /* Parameters: 1 if being called externally, or */ /* 0 if being called recursively; */ /* */ /* x coord of offset from the top */ /* right to 'plot' at (in milli- */ /* points); */ /* */ /* y coord of offset from the top */ /* right to 'plot' at (in milli- */ /* points); */ /* */ /* Pointer to a browser_data struct */ /* to represent in the Draw file; */ /* */ /* Pointer to the reformat_cell */ /* which is to be output; */ /* */ /* 1 to draw no backgrounds, else 0; */ /* */ /* A WimpRedrawWindowBlock pointer */ /* containing details on the area */ /* of the page to output; */ /* */ /* 1 to include any background */ /* image present on the browser's */ /* page, else 0; */ /* */ /* Pointer to an int to take the */ /* file size if that's all you want, */ /* or NULL to actually output the */ /* file; */ /* */ /* Pointer to an int in which the */ /* file size of the current chunk */ /* / cell (which may not be the */ /* whole file) is written. */ /* */ /* Assumes: Pointers must be valid and not */ /* NULL unless otherwise specified */ /* above. */ /*************************************************/ static int savedraw_create_r(int toplevel, int xorg, int yorg, browser_data * b, reformat_cell * d, int noback, WimpRedrawWindowBlock * r, int bgimages, int * size, int * tsize) { BBox wbox, sbox, fbox; int page_bottom, page_height; int osxorg, osyorg; int l = 0; /* Place the x and y origin in OS units into osxorg and osyorg */ convert_pair_to_os(xorg, yorg, &osxorg, &osyorg); sbox = r->redraw_area; /* Set sbox to hold the redraw rectangle details */ /* Convert the screen coord redraw rectangle into millipoints */ convert_box_to_points(&sbox, &fbox); /* Convert the screen coord redraw region into work */ /* area coords, putting the result in wbox. */ wbox = sbox; coords_box_toworkarea(&wbox, r); /* Now convert sbox to Draw coordinates. So, we have: */ /* */ /* sbox - The redraw area in Draw file coords; */ /* fbox - The redraw area in screen coords converted */ /* to millipoints; */ /* wbox - The redraw area in work area coords. */ sbox.xmin = DRAWMARGIN + OTD(sbox.xmin); sbox.ymin = DRAWMARGIN + OTD(sbox.ymin); sbox.xmax = DRAWMARGIN + OTD(sbox.xmax); sbox.ymax = DRAWMARGIN + OTD(sbox.ymax); /* Get the page bottom in work area coordinates, and the page height. */ /* This is really a printing only concept, where the visible_area */ /* BBox will in fact hold the entire page bounding box. The variables */ /* aren't used for anything else (at present, hence no 'if' wrapper). */ page_bottom = coords_y_toworkarea(r->visible_area.ymin, r); page_height = r->visible_area.ymax - r->visible_area.ymin; if (!noback) { int htop; if (!controls.swap_bars) htop = toolbars_button_height(b) + toolbars_url_height(b); else htop = toolbars_status_height(b); if (htop) htop += wimpt_dy(); /* If background images are not to be shown, or there's no image */ /* to tile on the background, set the background to a uniform */ /* colour. The 'if' statement implicitly calls the background */ /* image tiler. */ if ( !b->show_background || ( !bgimages || !image_tile_to_draw(b, r, 0, -htop, size) ) ) { if (!toplevel || redraw_backcol(b) != Redraw_Colour_White) { if ( !size && !savedraw_rectangle_fill(sbox.xmin, sbox.ymin, sbox.xmax - sbox.xmin, sbox.ymax - sbox.ymin - (toplevel ? OTD(htop) : 0), redraw_backcol(b)) ) return 0; *tsize += DSIZE_FRECT; } } } for (l = 0; l < d->nlines; l++) { /* If there are line chunks for this line, and the bounding box y */ /* coordinates lie within the redraw rectangle, process the line. */ if ( d->ldata[l].n && osyorg + d->ldata[l].y < wbox.ymax && osyorg + d->ldata[l].y + d->ldata[l].h > wbox.ymin ) { HStream * tp; /* Token Pointer */ fm_face h; int x, y; /* Plotting origin */ int keepx, keepy; /* Line's bottom left corner */ int base, i; char * dp; /* Data Pointer */ int cn; /* Chunk Number */ if (toplevel) { _swix(Hourglass_Percentage, _IN(0), (((wbox.ymax - osyorg - d->ldata[l].y) * 100) / (wbox.ymax - wbox.ymin)) - 1); } // if (printing == 1 && toplevel) // { // /* If printing = 1, it signals that we're not to split lines */ // /* over the bottom of the page. So if this line will drop */ // /* off the bottom, we need to be careful. This gets a bit */ // /* hacky now... */ // /* */ // /* To signal to the printing loop that a line was about to */ // /* be split but wasn't drawn, the top coord of the line is */ // /* returned in the xscroll field of the redraw block passed */ // /* to the function. The printing routines use this to work */ // /* out where to start the next redraw from. */ // /* */ // /* However, for large lines - e.g. very big images, tables, */ // /* or lines taller than the whole page - we either should, */ // /* or in the latter case must, split that line. So, if the */ // /* line is taller than 1/PrintSplitFraction of the page */ // /* height, will indeed be split over the page boundary. */ // /* */ // /* If you add code here, remember that xscroll must be */ // /* filled in eventually or the printing loop will exit, */ // /* assuming there's no more page to draw. */ // // if (osyorg + d->ldata[l].y < page_bottom) // { // if (d->ldata[l].h <= page_height / PrintSplitFraction) /* (See Print.h) */ // { // r->xscroll = osyorg + d->ldata[l].y + d->ldata[l].h; // return NULL; // } // // /* The effective 'else' case here has to be handled at the end */ // /* of printing, or you've just scrolled the page a long way to */ // /* right... */ // } // } // /* Put the base address of the line's chunks into cp, */ /* and point to its associated token in tp. */ cn = d->ldata[l].chunks; tp = d->cdata[cn].t; /* Get the x and y coordinates of the bottom left of the line in */ /* millipoints into keepx and keepy */ keepy = d->ldata[l].y; keepx = redraw_start_x(b, d, tp, l); convert_pair_to_points(keepx, keepy, &keepx, &keepy); x = coords_x_toscreen(0, r); y = coords_y_toscreen(0, r); convert_pair_to_points(x, y, &x, &y); /* Offset the line x and y coordinates by the origin passed into the */ /* function, and thus get the actual screen coordinates, in milli- */ /* points, into x and y. */ keepx += xorg; keepy += yorg; x += keepx; y += keepy; /* Set 'base' to hold the baseline offset in millipoints */ convert_to_points(d->ldata[l].b, &base); /* Loop round for all of the line chunks */ for ( i = 0; i < d->ldata[l].n && x < fbox.xmax; x += d->cdata[cn].w, i++, cn++ ) { if (x + d->cdata[cn].w > fbox.xmin) { /* Get the token address for this line chunk */ tp = d->cdata[cn].t; /* Deal with table tags */ if (tp->tagno == TAG_TABLE) { int oh; convert_to_points(d->ldata[l].h, &oh); /* Use of recursion for redraw... So need to keep this code block */ /* as a code block, don't try to collapse it down a level and */ /* merge in 'oh' above, etc. */ { /* In this case there are table streams hung from d->cdata */ table_stream * table = (table_stream *) tp; table_row * row = NULL; table_headdata * head = NULL; reformat_cell * cellarray = table->cells; reformat_cell * cell; int oldback = 0; int oldaa = 0; int oldbgimage = -1; int t_noback; BBox rbox; /* Redraw box - i.e. the cell BBox in window coordintes */ BBox * ibox; /* Used for intersection between cell BBox and current graphics rectangle */ BBox tbox; /* Used to plot the table outer border */ int cellindex; int cellcount; int cellmax = table->ColSpan * table->RowSpan; int swap; tbox.xmin = tbox.ymin = 0x1000000; tbox.xmax = tbox.ymax = 0; /* Only proceed if there are table cells to redraw */ if (cellarray) { row = table->List; cellcount = 0; while (row && cellcount < cellmax) { head = row->List; while (head && cellcount < cellmax) { switch (head->Tag) { case TagTableData: case TagTableHead: { cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { int cx, cy, cw, ch; cell = &cellarray[cellindex]; cx = x + cell->x; cy = y + cell->y + oh - cell->cellheight; cw = cell->cellwidth; ch = cell->cellheight; convert_pair_to_os(cx, cy, &cx, &cy); convert_pair_to_os(cw, ch, &cw, &ch); /* Update the table bounding box as required */ if (cx < tbox.xmin) tbox.xmin = cx; if (cy < tbox.ymin) tbox.ymin = cy; if (cx + cw > tbox.xmax) tbox.xmax = cx + cw; if (cy + ch > tbox.ymax) tbox.ymax = cy + ch; /* Closure of 'if (cellindex < cellmax)' */ } /* Closure of specific 'case' item */ } break; /* Closure of 'switch (head->Tag)' */ } cellcount ++; head = head->Next; /* Closure of 'while (head && ...)' */ } row = row->Next; /* Closure of 'while (row && ...)' */ } /* tbox doesn't take account of cell spacing yet - correct */ /* for this now. */ tbox.xmin -= table->cellspacing * 2; tbox.ymin -= table->cellspacing * 2; tbox.xmax += table->cellspacing * 2; tbox.ymax += table->cellspacing * 2; /* If the table has a background colour, deal with this */ if (TABLE_HAS_BGCOL(table)) { int tx, ty; int tw, th; /* Work out the x and y coordinates of the lower left hand pixel */ /* of the table border, and the width and height of the table */ /* including the border. */ tx = tbox.xmin; ty = tbox.ymin; tw = tbox.xmax - tbox.xmin; th = tbox.ymax - tbox.ymin; /* Output the background */ redraw_set_colour(TABLE_BGCOL(table)); if (!savedraw_rectangle_fill(OTD(tx), OTD(ty), OTD(tw), OTD(th), TABLE_BGCOL(table))) return 0; } /* Now output the table cells */ row = table->List; cellcount = 0; while (row && cellcount < cellmax) { head = row->List; while (head && cellcount < cellmax) { switch (head->Tag) { case TagTableData: case TagTableHead: { cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { cell = &cellarray[cellindex]; if (TD_HAS_BGCOL(head)) { oldback = b->background_colour; oldaa = b->antialias_colour; oldbgimage = b->background_image; b->background_colour = TD_BGCOL(head); b->antialias_colour = b->background_colour; b->background_image = -1; /* For now, no background images in table cells. */ t_noback = 0; } else t_noback = 1; /* Set the graphics rectangle up for the redraw */ rbox.xmin = x + cell->x; rbox.ymin = y + cell->y + oh - cell->cellheight; rbox.xmax = rbox.xmin + cell->cellwidth; rbox.ymax = rbox.ymin + cell->cellheight; /* Convert to OS units */ convert_box_to_os(&rbox, &rbox); /* If necessary, swap min and max coords */ if (rbox.xmax < rbox.xmin) swap = rbox.xmax, rbox.xmax = rbox.xmin, rbox.xmin = swap; if (rbox.ymax < rbox.ymin) swap = rbox.ymax, rbox.ymax = rbox.ymin, rbox.ymin = swap; /* See if the notional page redraw box intersects the overall redraw rectangle */ if (rbox.xmin == rbox.xmax || rbox.ymin == rbox.ymax) ibox = NULL; else ibox = intersection(&rbox, &r->redraw_area); if (ibox) { BBox oldrect; oldrect = r->redraw_area; r->redraw_area = *ibox; /* Recursive call to redraw the cell contents */ savedraw_create_r(0, keepx + cell->x, keepy + cell->y + oh, b, cell, t_noback, r, bgimages, size, tsize); /* Restore the redraw rectangle */ r->redraw_area = oldrect; } /* Restore any data altered in b */ if (!t_noback) { b->background_colour = oldback; b->antialias_colour = oldaa; b->background_image = oldbgimage; } /* Draw the slabbed in cell border. */ if (TABLE_BORDER(table)) { int dx = wimpt_dx(); int dy = wimpt_dy(); int max; int cx, cy; int cw, ch; /* Get the cell x,y and w,h in OS units from the redraw box. We want to */ /* use this information rather than cell->x etc. as the redraw box is */ /* the item that any internal redraws will have adhered to, including */ /* plotting cell backgrounds (if present). Thus, we want any table */ /* borders to be based on those same coordinates. */ cx = rbox.xmin; cy = rbox.ymin; cw = rbox.xmax - rbox.xmin; ch = rbox.ymax - rbox.ymin; /* Get the maximum horizontal OS to pixel scaling values, as this is */ /* used as a threshold for the 2D border / 3D border switching. */ if (dy > dx) max = dy; else max = dx; /* Don't do any actual drawing if the Choices don't say so. We have to */ /* do the calculation stuff above (well, most of it...!) so that the */ /* outer border plotter code (below) will work, whether inner borders */ /* are plotted or not. */ if (choices.table_inner != Choices_TableInner_Never) { /* For tables with a cell spacing greater than the OS unit */ /* to pixel scaling value, use a 3D border (depending on */ /* what is specified in the Choices). */ *tsize += DSIZE_FRECT * 4; if ( choices.table_inner != Choices_TableInner_Always2D && ( table->cellspacing > max || choices.table_inner == Choices_TableInner_Always3D ) ) { if (!savedraw_rectangle_fill(OTD(cx), OTD(cy - 2), OTD(cw), OTD(2), Redraw_Colour_AlmostWhite)) return 0; if (!savedraw_rectangle_fill(OTD(cx + cw), OTD(cy - 2), OTD(2), OTD(ch + 2), Redraw_Colour_AlmostWhite)) return 0; if (!savedraw_rectangle_fill(OTD(cx - 2), OTD(cy - 2), OTD(2), OTD(ch + 4), Redraw_Colour_PlinthGrey)) return 0; if (!savedraw_rectangle_fill(OTD(cx), OTD(cy + ch), OTD(cw + 2), OTD(2), Redraw_Colour_PlinthGrey)) return 0; } /* Otherwise use a black 2D border. If we've got half of the OS unit to */ /* pixel spacing available, then there'll be at least 1 pixel between */ /* all cells so we can draw in that gap. Otherwise, we must plot over */ /* the cell contents. */ else if (table->cellspacing >= (max >> 1)) { if (!savedraw_rectangle_fill(OTD(cx - 2), OTD(cy - 2), OTD(cw + 2), OTD(2), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(cx + cw), OTD(cy - 2), OTD(2), OTD(ch + 2), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(cx - 2), OTD(cy), OTD(2), OTD(ch + 2), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(cx), OTD(cy + ch), OTD(cw + 2), OTD(2), Redraw_Colour_Black)) return 0; } else { if (!savedraw_rectangle_fill(OTD(cx), OTD(cy), OTD(cw), OTD(2), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(cx + cw - 2), OTD(cy), OTD(2), OTD(ch), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(cx), OTD(cy), OTD(2), OTD(ch), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(cx), OTD(cy + ch - 2), OTD(cw), OTD(2), Redraw_Colour_Black)) return 0; } } } /* Closure of 'if (cellindex < cellmax)' */ } /* Closure of specific 'case' item */ } break; /* Closure of 'switch (head->Tag)' */ } cellcount ++; head = head->Next; /* Closure of 'while (head && ...)' */ } row = row->Next; /* Closure of 'while (row && ...)' */ } /* Right, now redraw the slabbed out outer table border */ if (TABLE_BORDER(table) && choices.table_outer != Choices_TableOuter_Never) { int dx = wimpt_dx(); int dy = wimpt_dy(); int max; int tx, ty; int tw, th; int tb; tb = TABLE_BORDER(table) * 2; /* 1 'web pixel' = 2 OS */ /* Work out the x and y coordinates of the lower left hand pixel */ /* of the table border, and the width and height of the table */ /* including the border. */ tx = tbox.xmin - tb; ty = tbox.ymin - tb; tw = tbox.xmax - tbox.xmin + tb * 2; th = tbox.ymax - tbox.ymin + tb * 2; /* Get the maximum horizontal OS to pixel scaling values, as this is */ /* used as a threshold for the 2D border / 3D border switching. */ if (dy > dx) max = dy; else max = dx; /* Use the same threshold value on the border as for internal cell borders */ if ( choices.table_outer != Choices_TableOuter_Always2D && ( table->cellspacing > max || choices.table_outer == Choices_TableOuter_Always3D ) ) { int x[4], y[4]; /* There are 8 corners to a plinth, 6------4 */ /* we draw using 8 triangles. Work |\ /| */ /* these out first in the arrays | 7--5 | */ /* declared above to make the | | | | */ /* plotting code tidier and avoid | 1--3 | */ /* unnecessary recalculation of |/ \| */ /* corner coordinates. 0------2 */ /* */ /* Note how there are only 4 unique x or y */ /* values, so that's all we need to work out. */ x[0] = OTD(tx); x[1] = OTD(tx + tb); x[2] = OTD(tx + tw - tb); x[3] = OTD(tx + tw); y[0] = OTD(ty); y[1] = OTD(ty + tb); y[2] = OTD(ty + th - tb); y[3] = OTD(ty + th); /* OK, now do the drawing. We need to be careful about */ /* the direction that the drawing occurs to ensure that */ /* adjacent diagonal lines meet up correctly. First, */ /* the bottom edge. */ if (!savedraw_triangle_fill(x[0], y[0], x[1], y[1], x[3], y[0], Redraw_Colour_PlinthGrey)) return 0; /* 0 -> 1 -> 2 */ if (!savedraw_triangle_fill(x[1], y[1], x[3], y[0], x[2], y[1], Redraw_Colour_PlinthGrey)) return 0; /* 1 -> 2 -> 3 */ /* Right hand edge */ if (!savedraw_triangle_fill(x[3], y[0], x[2], y[1], x[3], y[3], Redraw_Colour_PlinthGrey)) return 0; /* 2 -> 3 -> 4 */ if (!savedraw_triangle_fill(x[2], y[1], x[3], y[3], x[2], y[2], Redraw_Colour_PlinthGrey)) return 0; /* 3 -> 4 -> 5 */ /* Now the lighter section - the top edge first.*/ if (!savedraw_triangle_fill(x[3], y[3], x[2], y[2], x[0], y[3], Redraw_Colour_AlmostWhite)) return 0; /* 4 -> 5 -> 6 */ if (!savedraw_triangle_fill(x[2], y[2], x[0], y[3], x[1], y[2], Redraw_Colour_AlmostWhite)) return 0; /* 5 -> 6 -> 7 */ /* Finally, the left hand edge. */ if (!savedraw_triangle_fill(x[0], y[3], x[1], y[2], x[0], y[0], Redraw_Colour_AlmostWhite)) return 0; /* 6 -> 7 -> 0 */ if (!savedraw_triangle_fill(x[1], y[2], x[0], y[0], x[1], y[1], Redraw_Colour_AlmostWhite)) return 0; /* 7 -> 0 -> 1 */ } /* Otherwise, use a 2D outer border. */ else { if (!savedraw_rectangle_fill(OTD(tx), OTD(ty), OTD(tw), OTD(tb), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(tx), OTD(ty + th - tb), OTD(tw), OTD(tb), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(tx), OTD(ty), OTD(tb), OTD(th), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(tx + tw - tb), OTD(ty), OTD(tb), OTD(th), Redraw_Colour_Black)) return 0; } } /* Closure of 'if (cellarray)' */ } /* Closure of unconditional code block dealing with redrawing */ /* the body of a table. */ } } /* Deal with forms elements */ else if (tp->tagno == TAG_INPUT || tp->tagno == TAG_TEXTAREA || tp->tagno == TAG_SELECT) { int asize; /* A text-based element */ if ( tp->tagno == TAG_TEXTAREA || tp->tagno == TAG_SELECT || HtmlINPUTtype(tp) == inputtype_TEXT || HtmlINPUTtype(tp) == inputtype_PASSWORD ) { BBox box; int ox, oy; fm_face fh; convert_pair_to_os(x, y + base, &ox, &oy); fh = fm_find_token_font(b, tp, 0); fm_font_box(fh, &box); /* Set up the bounding box for a text area, with a minimum of 2 rows */ if (tp->tagno == TAG_TEXTAREA) { int r; int lh, lb; form_get_linesize(&box, &lh, &lb); r = tp->rows; if (r < 2) r = 2; box.ymin -= lh * (r - 1); /* ymin is already below the first line, so want to drop it by (rows - 1) more */ } /* Account for the borders */ box.ymin = box.ymin + oy - 8; box.ymax = box.ymax + oy + 8; box.xmin = ox + 4; convert_to_os(x + d->cdata[cn].w, &box.xmax); /* Account for a border */ box.xmax -= 4; asize = savedraw_input_field(b, tp, &box, redraw_token_colour(b, tp), tp->tagno == TAG_SELECT, size); if (!asize) return 0; else *tsize += asize; /* Now the text inside the item */ asize = savedraw_textarea_redraw(b, d->cdata[cn].t, &box, &r->redraw_area, fh, tp->tagno == TAG_TEXTAREA, tp->tagno == TAG_INPUT && HtmlINPUTtype(tp) == inputtype_PASSWORD, size); /* If the element is a SELECT field, it needs a menu icon too */ if (tp->tagno == TAG_SELECT) { int width, height, offset; BBox icon; /* Get the sprite size, work out a bounding box and plot */ /* this as a virtual icon. */ read_sprite_size("fgright", &width, &height); /* Work out the vertical offset */ offset = (box.ymax - box.ymin - height) / 2; /* We've not adjusted width and height for the border for the */ /* same reason as the '-2' below; in Redraw.c, this code will */ /* give the icon itself a border and the icon contents are */ /* centred within the bounding box that takes account of this */ /* - whereas here, we're just plotting the icon in a BBox */ /* which fits it closely at an absolute position. */ icon.xmin = box.xmax - width - 2; icon.ymin = box.ymin + offset; icon.xmax = icon.xmin + width; icon.ymax = icon.ymin + height; /* Output the icon */ asize = savedraw_switch(icon.xmin, icon.ymin, "fgright", size); if (!asize) return 0; else *tsize += asize; /* If we're on a non-grey background, give it a border */ if (redraw_backcol(b) != Redraw_Colour_BackGrey) { *tsize += DSIZE_FRECT * 4; if (!size) { int lw, lh; lw = icon.xmax - icon.xmin; lh = icon.ymax - icon.ymin; if (!savedraw_rectangle_fill(OTD(icon.xmin - 2), OTD(icon.ymin - 2), OTD(lw + 4), OTD(2), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(icon.xmin - 2), OTD(icon.ymin - 2), OTD(2), OTD(lh + 4), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(icon.xmin + lw), OTD(icon.ymin - 2), OTD(2), OTD(lh + 4), Redraw_Colour_Black)) return 0; if (!savedraw_rectangle_fill(OTD(icon.xmin - 2), OTD(icon.ymin + lh), OTD(lw + 4), OTD(2), Redraw_Colour_Black)) return 0; } } } } else switch(HtmlINPUTtype(tp)) { /* Graphics-based forms elements */ case inputtype_CHECKBOX: { int ox, oy; convert_pair_to_os(x, y + base, &ox, &oy); asize = savedraw_switch(ox, oy - 8, /* See redraw_switch, which subtracts 8 from the Y value itself */ form_get_field(b, d->cdata[cn].t) -> checked ? "fopton" : "foptoff", size); if (!asize) return 0; else *tsize += asize; } break; case inputtype_RADIO: { int ox, oy; convert_pair_to_os(x, y + base, &ox, &oy); asize = savedraw_switch(ox, oy - 8, /* See redraw_switch, which subtracts 8 from the Y value itself */ form_get_field(b, d->cdata[cn].t) -> checked ? "fradioon" : "fradiooff", size); if (!asize) return 0; else *tsize += asize; } break; case inputtype_IMAGE: goto do_image; /* See a short distance below */ case inputtype_HIDDEN: break; case inputtype_SUBMIT: /* SUBMIT same as RESET: no break */ case inputtype_BUTTON: /* Again, no break */ case inputtype_RESET: { BBox box; int fh, ox, oy, colour, th, tw; const char * p; p = form_button_text(tp); convert_pair_to_os(x, y + base, &ox, &oy); fh = fm_find_token_font(b, tp, 0); fm_font_box(fh, &box); th = box.ymax - box.ymin; box.ymin = box.ymin + oy - 8; box.ymax = box.ymax + oy + 8; box.xmin = ox + 4; convert_to_os(x + d->cdata[cn].w,&box.xmax); /* Account for a border */ box.xmax -= 4; /* Draw the button's plinth */ colour = redraw_token_colour(b, tp); asize = savedraw_button(b, tp, &box, 0, size); if (!asize) return 0; else *tsize += asize; /* Plot the text (which will always fit in a button), centred horizontally */ if (p && *p) { int length, end, offset; char null = '\0'; length = strlen(p); end = 0; while (end < length && p[end] != '\n') end++; if (fm_get_string_width(fh, p, 0x1000000, end - d->cdata[cn].o, -1, NULL, &tw)) return 0; offset = (d->cdata[cn].w - tw) / 2 + 4; if (offset < 0) offset = 0; /* Output the header */ if (!savedraw_texthdr(b, tp, PTD(x + offset), PTD(y + base), PTD(tw), OTD(th), 0, length, Redraw_Colour_BackGrey)) return 0; else *tsize += sizeof(draw_trfmtextstrhdr); /* (The count should include a terminator at this point) */ length++; /* Output the text ifself */ *tsize += (int) WordAlign(length); if (!savedraw_write_bytes(p, length - 1)) return 0; if (!savedraw_write_bytes(&null, 1)) return 0; if (length != (int) WordAlign(length)) { if (!savedraw_write_bytes(" ", (int) WordAlign(length) - length)) return 0; } } } break; } } /* Plot an imagem, OBJECT, EMBED or APPLET tag item */ else if (ISOBJECT(tp) || (tp->style & IMG)) { _kernel_oserror * e; BBox box; int ox, oy, o; int ok; int isobj; int objim; do_image: /* (This code is also used for form INPUT TYPE=IMAGE tags; see above) */ if (ISOBJECT(tp)) isobj = 1, objim = object_token_is_image(b, tp); else isobj = 0, objim = 0; convert_pair_to_os(x, y + base, &ox, &oy); e = isobj ? reformat_get_object_size(b, tp, &box) : reformat_get_image_size(b, tp, &box); if (!e) { draw_spristrhdr h; int img_size = 0; if (!isobj || objim) { img_size = image_draw_file_size(b, tp, 1); h.tag = draw_OBJSPRITE; h.size = image_draw_file_size(b, tp, 1) + sizeof(h); } /* Correct the coordinates for plotting */ ox -= box.xmin; box.xmin += ox; box.ymin += oy; box.xmax += ox; box.ymax += oy; h.bbox.xmin = OTD(box.xmin); h.bbox.ymin = OTD(box.ymin); h.bbox.xmax = OTD(box.xmax); h.bbox.ymax = OTD(box.ymax); /* Find the border width (if any) */ if (isobj) o = HtmlOBJECTborder(tp); else if (tp->style & IMG) o = tp->maxlen * 2; else o = 0; /* If we have an image to plot, output it */ if (img_size) { if (!savedraw_write_bytes((char *) &h, sizeof(h))) return 0; /* Output the image itself - do this before the */ /* image border (if any) so if there's some */ /* rounding when the Draw file is viewed scaled */ /* down, the border doesn't get hidden. */ ok = image_to_draw_file(b, r, tp, o + box.xmin, o + box.ymin, 1); if (!ok) return 0; } /* Otherwise, output a placeholder */ else { BBox pbox = box; const char * text; int asize; pbox.xmin += o; pbox.ymin += o; pbox.xmax -= pbox.xmin + o; pbox.ymax -= pbox.ymin + o; pbox.xmin &= ~1; pbox.ymin &= ~1; /* Work out what text to plot */ text = objim ? HtmlOBJECTstandby(tp) : tp->text; /* Output the placeholder */ asize = savedraw_draw_placeholder(b, r, &pbox, tp, text, NULL); if (!asize) return 0; else *tsize += asize; } /* Draw a border of tp->maxlen * 2 OS units width around an image. */ /* This comes from the image's BORDER attribute. */ if (o) { unsigned int c = redraw_token_colour(b, tp); *tsize += 4 * DSIZE_FRECT; if (!size) { if (!savedraw_rectangle_fill(OTD(box.xmin), OTD(box.ymin), OTD(box.xmax - box.xmin), OTD(o), c)) return 0; if (!savedraw_rectangle_fill(OTD(box.xmin), OTD(box.ymin), OTD(o), OTD(box.ymax - box.ymin), c)) return 0; if (!savedraw_rectangle_fill(OTD(box.xmin), OTD(box.ymax - o), OTD(box.xmax - box.xmin), OTD(o), c)) return 0; if (!savedraw_rectangle_fill(OTD(box.xmax - o), OTD(box.ymin), OTD(o), OTD(box.ymax - box.ymin), c)) return 0; } } } } /* Plot a horizontal rule */ else if (tp->style & HR) { int w, h, lmarg, ox, oy = 0; convert_to_os(y, &oy); lmarg = redraw_left_gap(b, d, tp); convert_to_os(d->cdata[cn].w, &w); /* Round width to a multiple of 2 and limit check it. */ /* Allow sizes greater than the available width, in */ /* which case align to the left and draw to whatever */ /* width was requested. */ w = w &~ 1; if (w < 2) w = 2; /* Deal with a size (height) specifier */ if (HR_HAS_SIZE(tp)) { /* Currently only recognise pixels */ switch (HR_SIZE_UNITS(tp)) { case UNITS_PIXELS: h = HR_SIZE(tp) * 2; break; /* (IMPORTANT: If adding extra units, ensure h ends up a multiple of 2) */ default: h = 4; break; } } else h = 4; /* Limit check the height */ if (h < 2) h = 2; /* Sort out the horizontal and vertical plotting offsets; */ /* centre vertically, and align horizontally as specified */ /* in the token. */ oy += ((d->ldata[l].h - h) / 2) &~3 - 4; oy += 6; ox = (coords_x_toscreen(lmarg + osxorg, r) &~1); /* Change ox and oy to draw coordinates */ ox = OTD(ox); oy = OTD(oy); /* Plot a black rule if NOSHADE is specified or the height */ /* or width are less than 4 OS units, else plot a '3D' */ /* rule. */ if (HR_NOSHADE(tp) || h < 4 || w < 4) { *tsize += DSIZE_FRECT; /* 1 rectangle */ if (!savedraw_rectangle_fill(ox, oy, OTD(w), OTD(h), 0)) return 0; } else { if (h == 4) { /* Simple 'groove' rule */ h = h / 2; w = OTD(w); h = OTD(h); *tsize += 2 * DSIZE_FRECT; /* 2 rectangles */ if (!savedraw_rectangle_fill(ox, oy, w, h, Redraw_Colour_MidGrey)) return 0; if (!savedraw_rectangle_fill(ox, oy - h, w, h, Redraw_Colour_AlmostWhite)) return 0; } else { /* 3D 'box' rule */ w = OTD(w); h = OTD(h); *tsize += 4 * DSIZE_FRECT; /* 4 rectangles */ if (!savedraw_rectangle_fill(ox, oy, w, OTD(2), Redraw_Colour_AlmostWhite)) return 0; if (!savedraw_rectangle_fill(ox + w - OTD(2), oy, OTD(2), h, Redraw_Colour_AlmostWhite)) return 0; if (!savedraw_rectangle_fill(ox, oy + h - OTD(2), w, OTD(2), Redraw_Colour_MidGrey)) return 0; if (!savedraw_rectangle_fill(ox, oy, OTD(2), h, Redraw_Colour_MidGrey)) return 0; } } } /* Plot a bullet point */ else if (ISBULLET(tp)) { int ox, oy; int asize; char spr[32]; sprintf(spr, "b%d\0", (tp->indent + bullets - 1) % bullets); convert_pair_to_os(x, y + base, &ox, &oy); asize = savedraw_switch(ox, oy, spr, size); if (!asize) return 0; else *tsize += asize; } /* Plot some text */ else { dp = tp->text; if (dp) { BBox size; int c, yofs, height; int drax, dray; int length; char null = '\0'; char * start; char * end; /* Find the font handle for the token, and its colour */ h = fm_find_token_font(b, tp, 0); c = redraw_token_colour(b, tp); /* Find the text height in OS units */ fm_font_box(h, &size); height = size.ymax - size.ymin; /* Work out the y offset to plot at */ if (ISSUP(tp)) { /* Shift baseline up for superscript text. The following */ /* will be for the SUP size text, remember... */ convert_to_points(height, &yofs); /* SUP height = normht * 3 / 5, so to get normal */ /* height from SUP do height * 5 / 3. Then want */ /* to get the height remaining and use this as an */ /* addition for the y positioning, so need to add */ /* (normht - hormht * 3 / 5) = normht * 2 / 5. */ /* This all simplifies out to height * 2 / 3, but */ /* this looks too high in practice, so it's taken */ /* down a bit from that. */ yofs = y + base + (yofs / 2); } else if (ISSUB(tp)) { /* Shift the baseline down a bit for subscript text */ yofs = y + (base * 4) / 5; } else yofs = y + base; /* Get the x and y coords in Draw coordinate units */ drax = PTD(x); dray = PTD(yofs); length = d->cdata[cn].l + 1; /* Can't have control characters in the Draw file output */ start = dp + d->cdata[cn].o; end = start + length - 2; while (length > 1 && *start && *start < ' ') start ++, length --; while (length > 1 && *end < ' ') end --, length --; if (length && d->cdata[cn].w && d->ldata[l].h) { /* Write the text item header */ *tsize += sizeof(draw_trfmtextstrhdr); if (!savedraw_texthdr(b, tp, drax, dray, PTD(d->cdata[cn].w), OTD(d->ldata[l].h), 0, length - 1, redraw_background_colour(b, c))) return 0; /* Write the text itself */ *tsize += (int) WordAlign(length); if (!savedraw_write_bytes(start, length - 1)) return 0; if (!savedraw_write_bytes(&null, 1)) return 0; if (length != (int) WordAlign(length)) { if (!savedraw_write_bytes(" ", (int) WordAlign(length) - length)) return 0; } /* Deal with underlining. The position should not be affected */ /* by SUB or SUP text. */ if ( ( ( ISLINK(tp) && b->underline_links ) || ISUNDERLINE(tp) ) && !(tp->tagno == TAG_TABLE) ) { /* Underline the item - set the colour, and start at the item's x coordinate... */ int ox, oy, w, c; /* Find the colour */ c = redraw_token_colour(b, tp); /* Work out the coordinates (in OS units) */ convert_pair_to_os(x, y + base, &ox, &oy); oy -= 7; /* Draw the 'line' as a rectangle */ ox = OTD(ox); oy = OTD(oy); w = PTD(d->cdata[cn].w); *tsize += DSIZE_FRECT; if (!savedraw_rectangle_fill(ox, oy, w, OTD(2), c)) return 0; } /* Deal with STRIKE text. This needs to have the strikethrough */ /* line through the text middle, as opposed to following the */ /* body text font baseline (so SUB and SUP *will* have an */ /* effect on the positioning). */ if (ISSTRIKE(tp) && !(tp->tagno == TAG_TABLE)) { int ox, oy, w, c; int hs; c = redraw_token_colour(b, tp); convert_pair_to_os(x, yofs, &ox, &oy); hs = height / 4; oy += hs; ox = OTD(ox); oy = OTD(oy); w = PTD(d->cdata[cn].w); *tsize += DSIZE_FRECT; if (!savedraw_rectangle_fill(ox, oy, w, OTD(2), c)) return 0; } } } } /* Closure of long 'if' checking if the current chunk */ /* lies partially or entirely within the redraw area. */ /* If it does, the code above executes. */ } /* Closure of 'for' looping round chunks on a given line */ /* that lies partially or entirely within the redraw */ /* area. */ } /* Closure of long 'if' checking if the current line lies */ /* partially or entirely within the redraw area. The code */ /* above executes if it does. */ } /* Closure of 'for' looping for all lines in the document. */ } if (size) *size = *tsize; return 1; } /*************************************************/ /* savedraw_save_draw() */ /* */ /* Save a given browser page as a Draw file. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the page to save as */ /* a Draw file; */ /* */ /* Pointer to the pathname to save */ /* to; */ /* */ /* 1 to include background image */ /* tiling if there is such an image, */ /* else 0 (0 recommended!). */ /*************************************************/ _kernel_oserror * savedraw_save_draw(browser_data * b, const char * pathname, int bgimages) { int success = 0; /* If we seem to have left a file open, close it */ if (outfile) { fclose(outfile); outfile = NULL; } /* Only proceed if we've got a pathname */ if (pathname && *pathname) { _swix(Hourglass_On, 0); save_record_path(pathname); /* Open the file */ outfile = fopen(pathname, "wb"); if (!outfile) RetLastE; /* Create the file */ success = savedraw_create(b, bgimages, NULL); fclose(outfile); outfile = NULL; _swix(Hourglass_Off, 0); if (!success) RetLastE; /* Set the filetype */ return _swix(OS_File, _INR(0,2) | _INR(4,5), 18, pathname, FileType_DRAW); } return NULL; } /*************************************************/ /* savedraw_draw_size() */ /* */ /* Returns the size of file that would be */ /* written by savedraw_save_draw for the given */ /* browser. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the page to save as */ /* a Draw file, for which the file */ /* size is to be returned; */ /* */ /* 1 to include background image */ /* tiling in the count if there is */ /* such an image, else 0. */ /* */ /* Returns: Size of file that would be */ /* written by savedraw_save_draw for */ /* the given browser. */ /*************************************************/ int savedraw_draw_size(browser_data * b, int bgimages) { int size; // savedraw_create(b, bgimages, &size); size = 4096; return size; }