Subversion Repositories Kolibri OS

Rev

Rev 3584 | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright 2004-2008 James Bursa <bursa@users.sourceforge.net>
  3.  * Copyright 2004-2007 John M Bell <jmb202@ecs.soton.ac.uk>
  4.  * Copyright 2004-2007 Richard Wilson <info@tinct.net>
  5.  * Copyright 2005-2006 Adrian Lees <adrianl@users.sourceforge.net>
  6.  * Copyright 2006 Rob Kendrick <rjek@netsurf-browser.org>
  7.  * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
  8.  * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
  9.  *
  10.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  11.  *
  12.  * NetSurf is free software; you can redistribute it and/or modify
  13.  * it under the terms of the GNU General Public License as published by
  14.  * the Free Software Foundation; version 2 of the License.
  15.  *
  16.  * NetSurf is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  23.  */
  24.  
  25. /** \file
  26.  * Redraw of a CONTENT_HTML (implementation).
  27.  */
  28.  
  29. #include <assert.h>
  30. #include <stdbool.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <math.h>
  34. #include <dom/dom.h>
  35. #include "utils/config.h"
  36. #include "content/content_protected.h"
  37. #include "css/css.h"
  38. #include "css/utils.h"
  39. #include "desktop/plotters.h"
  40. #include "desktop/selection.h"
  41. #include "desktop/options.h"
  42. #include "desktop/print.h"
  43. #include "desktop/scrollbar.h"
  44. #include "image/bitmap.h"
  45. #include "render/box.h"
  46. #include "render/font.h"
  47. #include "render/form.h"
  48. #include "render/html_internal.h"
  49. #include "render/layout.h"
  50. #include "render/search.h"
  51. #include "utils/log.h"
  52. #include "utils/messages.h"
  53. #include "utils/utils.h"
  54.  
  55.  
  56.  
  57.  
  58. bool html_redraw_debug = false;
  59.  
  60. /**
  61.  * Determine if a box has a background that needs drawing
  62.  *
  63.  * \param box  Box to consider
  64.  * \return True if box has a background, false otherwise.
  65.  */
  66. static bool html_redraw_box_has_background(struct box *box)
  67. {
  68.         if (box->background != NULL)
  69.                 return true;
  70.  
  71.         if (box->style != NULL) {
  72.                 css_color colour;
  73.  
  74.                 css_computed_background_color(box->style, &colour);
  75.  
  76.                 if (nscss_color_is_transparent(colour) == false)
  77.                         return true;
  78.         }
  79.  
  80.         return false;
  81. }
  82.  
  83. /**
  84.  * Find the background box for a box
  85.  *
  86.  * \param box  Box to find background box for
  87.  * \return Pointer to background box, or NULL if there is none
  88.  */
  89. static struct box *html_redraw_find_bg_box(struct box *box)
  90. {
  91.         /* Thanks to backwards compatibility, CSS defines the following:
  92.          *
  93.          * + If the box is for the root element and it has a background,
  94.          *   use that (and then process the body box with no special case)
  95.          * + If the box is for the root element and it has no background,
  96.          *   then use the background (if any) from the body element as if
  97.          *   it were specified on the root. Then, when the box for the body
  98.          *   element is processed, ignore the background.
  99.          * + For any other box, just use its own styling.
  100.          */
  101.         if (box->parent == NULL) {
  102.                 /* Root box */
  103.                 if (html_redraw_box_has_background(box))
  104.                         return box;
  105.  
  106.                 /* No background on root box: consider body box, if any */
  107.                 if (box->children != NULL) {
  108.                         if (html_redraw_box_has_background(box->children))
  109.                                 return box->children;
  110.                 }
  111.         } else if (box->parent != NULL && box->parent->parent == NULL) {
  112.                 /* Body box: only render background if root has its own */
  113.                 if (html_redraw_box_has_background(box) &&
  114.                                 html_redraw_box_has_background(box->parent))
  115.                         return box;
  116.         } else {
  117.                 /* Any other box */
  118.                 if (html_redraw_box_has_background(box))
  119.                         return box;
  120.         }
  121.  
  122.         return NULL;
  123. }
  124.  
  125. /**
  126.  * Redraw a short text string, complete with highlighting
  127.  * (for selection/search)
  128.  *
  129.  * \param  utf8_text  pointer to UTF-8 text string
  130.  * \param  utf8_len   length of string, in bytes
  131.  * \param  offset     byte offset within textual representation
  132.  * \param  space      width of space that follows string (0 = no space)
  133.  * \param  fstyle     text style to use (pass text size unscaled)
  134.  * \param  x          x ordinate at which to plot text
  135.  * \param  y          y ordinate at which to plot text
  136.  * \param  clip       pointer to current clip rectangle
  137.  * \param  height     height of text string
  138.  * \param  scale      current display scale (1.0 = 100%)
  139.  * \param  excluded   exclude this text string from the selection
  140.  * \param  ctx        current redraw context
  141.  * \return true iff successful and redraw should proceed
  142.  */
  143.  
  144. bool text_redraw(const char *utf8_text, size_t utf8_len,
  145.                 size_t offset, int space, const plot_font_style_t *fstyle,
  146.                 int x, int y, const struct rect *clip, int height,
  147.                 float scale, bool excluded, struct content *c,
  148.                 const struct selection *sel, struct search_context *search,
  149.                 const struct redraw_context *ctx)
  150. {
  151.         const struct plotter_table *plot = ctx->plot;
  152.         bool highlighted = false;
  153.         plot_font_style_t plot_fstyle = *fstyle;
  154.  
  155.         /* Need scaled text size to pass to plotters */
  156.         plot_fstyle.size *= scale;
  157.  
  158.         /* is this box part of a selection? */
  159.         if (!excluded && ctx->interactive == true) {
  160.                 unsigned len = utf8_len + (space ? 1 : 0);
  161.                 unsigned start_idx;
  162.                 unsigned end_idx;
  163.  
  164.                 /* first try the browser window's current selection */
  165.                 if (selection_defined(sel) && selection_highlighted(sel,
  166.                                         offset, offset + len,
  167.                                         &start_idx, &end_idx)) {
  168.                         highlighted = true;
  169.                 }
  170.  
  171.                 /* what about the current search operation, if any? */
  172.                 if (!highlighted && (search != NULL) &&
  173.                                 search_term_highlighted(c,
  174.                                                 offset, offset + len,
  175.                                                 &start_idx, &end_idx,
  176.                                                 search)) {
  177.                         highlighted = true;
  178.                 }
  179.  
  180.                 /* \todo make search terms visible within selected text */
  181.                 if (highlighted) {
  182.                         struct rect r;
  183.                         unsigned endtxt_idx = end_idx;
  184.                         bool clip_changed = false;
  185.                         bool text_visible = true;
  186.                         int startx, endx;
  187.                         plot_style_t *pstyle_fill_hback = plot_style_fill_white;
  188.                         plot_font_style_t fstyle_hback = plot_fstyle;
  189.  
  190.                         if (end_idx > utf8_len) {
  191.                                 /* adjust for trailing space, not present in
  192.                                  * utf8_text */
  193.                                 assert(end_idx == utf8_len + 1);
  194.                                 endtxt_idx = utf8_len;
  195.                         }
  196.  
  197.                         if (!nsfont.font_width(fstyle, utf8_text, start_idx,
  198.                                         &startx))
  199.                                 startx = 0;
  200.  
  201.                         if (!nsfont.font_width(fstyle, utf8_text, endtxt_idx,
  202.                                         &endx))
  203.                                 endx = 0;
  204.  
  205.                         /* is there a trailing space that should be highlighted
  206.                          * as well? */
  207.                         if (end_idx > utf8_len) {
  208.                                         endx += space;
  209.                         }
  210.  
  211.                         if (scale != 1.0) {
  212.                                 startx *= scale;
  213.                                 endx *= scale;
  214.                         }
  215.  
  216.                         /* draw any text preceding highlighted portion */
  217.                         if (start_idx > 0 &&
  218.                                 !plot->text(x, y + (int)(height * 0.75 * scale),
  219.                                                 utf8_text, start_idx,
  220.                                                 &plot_fstyle))
  221.                                 return false;
  222.  
  223.                         /* decide whether highlighted portion is to be
  224.                          * white-on-black or black-on-white */
  225.                         if ((fstyle->background & 0x808080) == 0x808080)
  226.                                 pstyle_fill_hback = plot_style_fill_black;
  227.  
  228.                         /* highlighted portion */
  229.                         if (!plot->rectangle(x + startx, y, x + endx,
  230.                                         y + height * scale,
  231.                                         pstyle_fill_hback))
  232.                                 return false;
  233.  
  234.                         if (start_idx > 0) {
  235.                                 int px0 = max(x + startx, clip->x0);
  236.                                 int px1 = min(x + endx, clip->x1);
  237.  
  238.                                 if (px0 < px1) {
  239.                                         r.x0 = px0;
  240.                                         r.y0 = clip->y0;
  241.                                         r.x1 = px1;
  242.                                         r.y1 = clip->y1;
  243.                                         if (!plot->clip(&r))
  244.                                                 return false;
  245.                                         clip_changed = true;
  246.                                 } else {
  247.                                         text_visible = false;
  248.                                 }
  249.                         }
  250.  
  251.                         fstyle_hback.background =
  252.                                 pstyle_fill_hback->fill_colour;
  253.                         fstyle_hback.foreground =
  254.                                 pstyle_fill_hback->fill_colour ^ 0xffffff;
  255.  
  256.                         if (text_visible &&
  257.                                 !plot->text(x, y + (int)(height * 0.75 * scale),
  258.                                                 utf8_text, endtxt_idx,
  259.                                                 &fstyle_hback))
  260.                                 return false;
  261.  
  262.                         /* draw any text succeeding highlighted portion */
  263.                         if (endtxt_idx < utf8_len) {
  264.                                 int px0 = max(x + endx, clip->x0);
  265.                                 if (px0 < clip->x1) {
  266.  
  267.                                         r.x0 = px0;
  268.                                         r.y0 = clip->y0;
  269.                                         r.x1 = clip->x1;
  270.                                         r.y1 = clip->y1;
  271.                                         if (!plot->clip(&r))
  272.                                                 return false;
  273.  
  274.                                         clip_changed = true;
  275.  
  276.                                         if (!plot->text(x, y + (int)
  277.                                                 (height * 0.75 * scale),
  278.                                                 utf8_text, utf8_len,
  279.                                                 &plot_fstyle))
  280.                                                 return false;
  281.                                 }
  282.                         }
  283.  
  284.                         if (clip_changed &&
  285.                                 !plot->clip(clip))
  286.                                 return false;
  287.                 }
  288.         }
  289.  
  290.         if (!highlighted) {
  291.                 if (!plot->text(x, y + (int) (height * 0.75 * scale),
  292.                                 utf8_text, utf8_len,
  293.                                 &plot_fstyle))
  294.                         return false;
  295.         }
  296.         return true;
  297. }
  298.  
  299. static plot_style_t plot_style_bdr = {
  300.         .stroke_type = PLOT_OP_TYPE_DASH,
  301. };
  302. static plot_style_t plot_style_fillbdr = {
  303.         .fill_type = PLOT_OP_TYPE_SOLID,
  304. };
  305. static plot_style_t plot_style_fillbdr_dark = {
  306.         .fill_type = PLOT_OP_TYPE_SOLID,
  307. };
  308. static plot_style_t plot_style_fillbdr_light = {
  309.         .fill_type = PLOT_OP_TYPE_SOLID,
  310. };
  311. static plot_style_t plot_style_fillbdr_ddark = {
  312.         .fill_type = PLOT_OP_TYPE_SOLID,
  313. };
  314. static plot_style_t plot_style_fillbdr_dlight = {
  315.         .fill_type = PLOT_OP_TYPE_SOLID,
  316. };
  317.  
  318. /**
  319.  * Draw one border.
  320.  *
  321.  * \param  side         index of border side (TOP, RIGHT, BOTTOM, LEFT)
  322.  * \param  p            array of precomputed border vertices
  323.  * \param  c            colour for border
  324.  * \param  style        border line style
  325.  * \param  thickness    border thickness
  326.  * \param  rectangular  whether border is rectangular
  327.  * \param  ctx          current redraw context
  328.  * \return true if successful, false otherwise
  329.  */
  330.  
  331. static bool html_redraw_border_plot(const int side, const int *p, colour c,
  332.                 enum css_border_style_e style, int thickness, bool rectangular,
  333.                 const struct rect *clip, const struct redraw_context *ctx)
  334. {
  335.         const struct plotter_table *plot = ctx->plot;
  336.         int z[8]; /* Vertices of border part */
  337.         unsigned int light = side;
  338.         plot_style_t *plot_style_bdr_in;
  339.         plot_style_t *plot_style_bdr_out;
  340.  
  341.         if (c == NS_TRANSPARENT)
  342.                 return true;
  343.  
  344.         plot_style_bdr.stroke_type = PLOT_OP_TYPE_DASH;
  345.         plot_style_bdr.stroke_colour = c;
  346.         plot_style_bdr.stroke_width = thickness;
  347.         plot_style_fillbdr.fill_colour = c;
  348.         plot_style_fillbdr_dark.fill_colour = darken_colour(c);
  349.         plot_style_fillbdr_light.fill_colour = lighten_colour(c);
  350.         plot_style_fillbdr_ddark.fill_colour = double_darken_colour(c);
  351.         plot_style_fillbdr_dlight.fill_colour = double_lighten_colour(c);
  352.  
  353.         switch (style) {
  354.         case CSS_BORDER_STYLE_DOTTED:
  355.                 plot_style_bdr.stroke_type = PLOT_OP_TYPE_DOT;
  356.                 /* fall through */
  357.         case CSS_BORDER_STYLE_DASHED:
  358.                 if (!plot->line((p[0] + p[2]) / 2,
  359.                                 (p[1] + p[3]) / 2,
  360.                                 (p[4] + p[6]) / 2,
  361.                                 (p[5] + p[7]) / 2,
  362.                                 &plot_style_bdr))
  363.                         return false;
  364.                 break;
  365.  
  366.         case CSS_BORDER_STYLE_SOLID:
  367.                 /* fall through to default */
  368.         default:
  369.                 if (rectangular || thickness == 1) {
  370.                         int x0, y0, x1, y1;
  371.                         if (side == TOP || side == RIGHT) {
  372.                                 x0 = p[2];      y0 = p[3];
  373.                                 x1 = p[6];      y1 = p[7];
  374.                                 x1 = ((side == TOP) && (p[4] - p[6] != 0)) ?
  375.                                         x1 + p[4] - p[6] : x1;
  376.                         } else {
  377.                                 x0 = p[6];      y0 = p[7];
  378.                                 x1 = p[2];      y1 = p[3];
  379.                                 y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ?
  380.                                         y1 + p[1] - p[3] : y1;
  381.                         }
  382.                         /* find intersection of clip rectangle and border */
  383.                         x0 = (clip->x0 > x0) ? clip->x0 : x0;
  384.                         y0 = (clip->y0 > y0) ? clip->y0 : y0;
  385.                         x1 = (clip->x1 < x1) ? clip->x1 : x1;
  386.                         y1 = (clip->y1 < y1) ? clip->y1 : y1;
  387.                         if ((x0 < x1) && (y0 < y1)) {
  388.                                 /* valid clip rectangles only */
  389.                                 if (!plot->rectangle(x0, y0, x1, y1,
  390.                                                 &plot_style_fillbdr))
  391.                                         return false;
  392.                         }
  393.                 } else {
  394.                         if (!plot->polygon(p, 4, &plot_style_fillbdr))
  395.                                 return false;
  396.                 }
  397.                 break;
  398.  
  399.         case CSS_BORDER_STYLE_DOUBLE:
  400.                 z[0] = p[0];
  401.                 z[1] = p[1];
  402.                 z[2] = (p[0] * 2 + p[2]) / 3;
  403.                 z[3] = (p[1] * 2 + p[3]) / 3;
  404.                 z[4] = (p[6] * 2 + p[4]) / 3;
  405.                 z[5] = (p[7] * 2 + p[5]) / 3;
  406.                 z[6] = p[6];
  407.                 z[7] = p[7];
  408.                 if (!plot->polygon(z, 4, &plot_style_fillbdr))
  409.                         return false;
  410.                 z[0] = p[2];
  411.                 z[1] = p[3];
  412.                 z[2] = (p[2] * 2 + p[0]) / 3;
  413.                 z[3] = (p[3] * 2 + p[1]) / 3;
  414.                 z[4] = (p[4] * 2 + p[6]) / 3;
  415.                 z[5] = (p[5] * 2 + p[7]) / 3;
  416.                 z[6] = p[4];
  417.                 z[7] = p[5];
  418.                 if (!plot->polygon(z, 4, &plot_style_fillbdr))
  419.                         return false;
  420.                 break;
  421.  
  422.         case CSS_BORDER_STYLE_GROOVE:
  423.                 light = 3 - light;
  424.                 /* fall through */
  425.         case CSS_BORDER_STYLE_RIDGE:
  426.                 /* choose correct colours for each part of the border line */
  427.                 if (light <= 1) {
  428.                         plot_style_bdr_in = &plot_style_fillbdr_dark;
  429.                         plot_style_bdr_out = &plot_style_fillbdr_light;
  430.                 } else {
  431.                         plot_style_bdr_in = &plot_style_fillbdr_light;
  432.                         plot_style_bdr_out = &plot_style_fillbdr_dark;
  433.                 }
  434.  
  435.                 /* Render border */
  436.                 if ((rectangular || thickness == 2) && thickness != 1) {
  437.                         /* Border made up from two parts and can be plotted
  438.                          * with rectangles */
  439.                         int x0, y0, x1, y1;
  440.  
  441.                         /* First part */
  442.                         if (side == TOP || side == RIGHT) {
  443.                                 x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2;
  444.                                 x1 = p[6];              y1 = p[7];
  445.                         } else {
  446.                                 x0 = p[6];              y0 = p[7];
  447.                                 x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2;
  448.                         }
  449.                         /* find intersection of clip rectangle and border */
  450.                         x0 = (clip->x0 > x0) ? clip->x0 : x0;
  451.                         y0 = (clip->y0 > y0) ? clip->y0 : y0;
  452.                         x1 = (clip->x1 < x1) ? clip->x1 : x1;
  453.                         y1 = (clip->y1 < y1) ? clip->y1 : y1;
  454.                         if ((x0 < x1) && (y0 < y1)) {
  455.                                 /* valid clip rectangles only */
  456.                                 if (!plot->rectangle(x0, y0, x1, y1,
  457.                                                 plot_style_bdr_in))
  458.                                         return false;
  459.                         }
  460.  
  461.                         /* Second part */
  462.                         if (side == TOP || side == RIGHT) {
  463.                                 x0 = p[2];              y0 = p[3];
  464.                                 x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2;
  465.                         } else {
  466.                                 x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2;
  467.                                 x1 = p[2];              y1 = p[3];
  468.                         }
  469.                         /* find intersection of clip rectangle and border */
  470.                         x0 = (clip->x0 > x0) ? clip->x0 : x0;
  471.                         y0 = (clip->y0 > y0) ? clip->y0 : y0;
  472.                         x1 = (clip->x1 < x1) ? clip->x1 : x1;
  473.                         y1 = (clip->y1 < y1) ? clip->y1 : y1;
  474.                         if ((x0 < x1) && (y0 < y1)) {
  475.                                 /* valid clip rectangles only */
  476.                                 if (!plot->rectangle(x0, y0, x1, y1,
  477.                                                 plot_style_bdr_out))
  478.                                         return false;
  479.                         }
  480.                 } else if (thickness == 1) {
  481.                         /* Border made up from one part which can be plotted
  482.                          * as a rectangle */
  483.                         int x0, y0, x1, y1;
  484.                         if (side == TOP || side == RIGHT) {
  485.                                 x0 = p[2];      y0 = p[3];
  486.                                 x1 = p[6];      y1 = p[7];
  487.                                 x1 = ((side == TOP) && (p[4] - p[6] != 0)) ?
  488.                                         x1 + p[4] - p[6] : x1;
  489.                                 /* find intersection of clip rectangle and
  490.                                  * border */
  491.                                 x0 = (clip->x0 > x0) ? clip->x0 : x0;
  492.                                 y0 = (clip->y0 > y0) ? clip->y0 : y0;
  493.                                 x1 = (clip->x1 < x1) ? clip->x1 : x1;
  494.                                 y1 = (clip->y1 < y1) ? clip->y1 : y1;
  495.                                 if ((x0 < x1) && (y0 < y1)) {
  496.                                         /* valid clip rectangles only */
  497.                                         if (!plot->rectangle(x0, y0, x1, y1,
  498.                                                         plot_style_bdr_in))
  499.                                                 return false;
  500.                                 }
  501.                         } else {
  502.                                 x0 = p[6];      y0 = p[7];
  503.                                 x1 = p[2];      y1 = p[3];
  504.                                 y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ?
  505.                                         y1 + p[1] - p[3] : y1;
  506.                                 /* find intersection of clip rectangle and
  507.                                  * border */
  508.                                 x0 = (clip->x0 > x0) ? clip->x0 : x0;
  509.                                 y0 = (clip->y0 > y0) ? clip->y0 : y0;
  510.                                 x1 = (clip->x1 < x1) ? clip->x1 : x1;
  511.                                 y1 = (clip->y1 < y1) ? clip->y1 : y1;
  512.                                 if ((x0 < x1) && (y0 < y1)) {
  513.                                         /* valid clip rectangles only */
  514.                                         if (!plot->rectangle(x0, y0, x1, y1,
  515.                                                         plot_style_bdr_out))
  516.                                                 return false;
  517.                                 }
  518.                         }
  519.                 } else {
  520.                         /* Border made up from two parts and can't be plotted
  521.                          * with rectangles */
  522.                         z[0] = p[0];
  523.                         z[1] = p[1];
  524.                         z[2] = (p[0] + p[2]) / 2;
  525.                         z[3] = (p[1] + p[3]) / 2;
  526.                         z[4] = (p[6] + p[4]) / 2;
  527.                         z[5] = (p[7] + p[5]) / 2;
  528.                         z[6] = p[6];
  529.                         z[7] = p[7];
  530.                         if (!plot->polygon(z, 4, plot_style_bdr_in))
  531.                                 return false;
  532.                         z[0] = p[2];
  533.                         z[1] = p[3];
  534.                         z[6] = p[4];
  535.                         z[7] = p[5];
  536.                         if (!plot->polygon(z, 4, plot_style_bdr_out))
  537.                                 return false;
  538.                 }
  539.                 break;
  540.  
  541.         case CSS_BORDER_STYLE_INSET:
  542.                 light = (light + 2) % 4;
  543.                 /* fall through */
  544.         case CSS_BORDER_STYLE_OUTSET:
  545.                 /* choose correct colours for each part of the border line */
  546.                 switch (light) {
  547.                 case 0:
  548.                         plot_style_bdr_in = &plot_style_fillbdr_light;
  549.                         plot_style_bdr_out = &plot_style_fillbdr_dlight;
  550.                         break;
  551.                 case 1:
  552.                         plot_style_bdr_in = &plot_style_fillbdr_ddark;
  553.                         plot_style_bdr_out = &plot_style_fillbdr_dark;
  554.                         break;
  555.                 case 2:
  556.                         plot_style_bdr_in = &plot_style_fillbdr_dark;
  557.                         plot_style_bdr_out = &plot_style_fillbdr_ddark;
  558.                         break;
  559.                 case 3:
  560.                         plot_style_bdr_in = &plot_style_fillbdr_dlight;
  561.                         plot_style_bdr_out = &plot_style_fillbdr_light;
  562.                         break;
  563.                 default:
  564.                         plot_style_bdr_in = &plot_style_fillbdr;
  565.                         plot_style_bdr_out = &plot_style_fillbdr;
  566.                         break;
  567.                 }
  568.  
  569.                 /* Render border */
  570.                 if ((rectangular || thickness == 2) && thickness != 1) {
  571.                         /* Border made up from two parts and can be plotted
  572.                          * with rectangles */
  573.                         int x0, y0, x1, y1;
  574.  
  575.                         /* First part */
  576.                         if (side == TOP || side == RIGHT) {
  577.                                 x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2;
  578.                                 x1 = p[6];              y1 = p[7];
  579.                         } else {
  580.                                 x0 = p[6];              y0 = p[7];
  581.                                 x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2;
  582.                         }
  583.                         /* find intersection of clip rectangle and border */
  584.                         x0 = (clip->x0 > x0) ? clip->x0 : x0;
  585.                         y0 = (clip->y0 > y0) ? clip->y0 : y0;
  586.                         x1 = (clip->x1 < x1) ? clip->x1 : x1;
  587.                         y1 = (clip->y1 < y1) ? clip->y1 : y1;
  588.                         if ((x0 < x1) && (y0 < y1)) {
  589.                                 /* valid clip rectangles only */
  590.                                 if (!plot->rectangle(x0, y0, x1, y1,
  591.                                                 plot_style_bdr_in))
  592.                                         return false;
  593.                         }
  594.  
  595.                         /* Second part */
  596.                         if (side == TOP || side == RIGHT) {
  597.                                 x0 = p[2];              y0 = p[3];
  598.                                 x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2;
  599.                         } else {
  600.                                 x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2;
  601.                                 x1 = p[2];              y1 = p[3];
  602.                         }
  603.                         /* find intersection of clip rectangle and border */
  604.                         x0 = (clip->x0 > x0) ? clip->x0 : x0;
  605.                         y0 = (clip->y0 > y0) ? clip->y0 : y0;
  606.                         x1 = (clip->x1 < x1) ? clip->x1 : x1;
  607.                         y1 = (clip->y1 < y1) ? clip->y1 : y1;
  608.                         if ((x0 < x1) && (y0 < y1)) {
  609.                                 /* valid clip rectangles only */
  610.                                 if (!plot->rectangle(x0, y0, x1, y1,
  611.                                                 plot_style_bdr_out))
  612.                                         return false;
  613.                         }
  614.                 } else if (thickness == 1) {
  615.                         /* Border made up from one part which can be plotted
  616.                          * as a rectangle */
  617.                         int x0, y0, x1, y1;
  618.                         if (side == TOP || side == RIGHT) {
  619.                                 x0 = p[2];      y0 = p[3];
  620.                                 x1 = p[6];      y1 = p[7];
  621.                                 x1 = ((side == TOP) && (p[4] - p[6] != 0)) ?
  622.                                         x1 + p[4] - p[6] : x1;
  623.                                 /* find intersection of clip rectangle and
  624.                                  * border */
  625.                                 x0 = (clip->x0 > x0) ? clip->x0 : x0;
  626.                                 y0 = (clip->y0 > y0) ? clip->y0 : y0;
  627.                                 x1 = (clip->x1 < x1) ? clip->x1 : x1;
  628.                                 y1 = (clip->y1 < y1) ? clip->y1 : y1;
  629.                                 if ((x0 < x1) && (y0 < y1)) {
  630.                                         /* valid clip rectangles only */
  631.                                         if (!plot->rectangle(x0, y0, x1, y1,
  632.                                                         plot_style_bdr_in))
  633.                                                 return false;
  634.                                 }
  635.                         } else {
  636.                                 x0 = p[6];      y0 = p[7];
  637.                                 x1 = p[2];      y1 = p[3];
  638.                                 y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ?
  639.                                         y1 + p[1] - p[3] : y1;
  640.                                 /* find intersection of clip rectangle and
  641.                                  * border */
  642.                                 x0 = (clip->x0 > x0) ? clip->x0 : x0;
  643.                                 y0 = (clip->y0 > y0) ? clip->y0 : y0;
  644.                                 x1 = (clip->x1 < x1) ? clip->x1 : x1;
  645.                                 y1 = (clip->y1 < y1) ? clip->y1 : y1;
  646.                                 if ((x0 < x1) && (y0 < y1)) {
  647.                                         /* valid clip rectangles only */
  648.                                         if (!plot->rectangle(x0, y0, x1, y1,
  649.                                                         plot_style_bdr_out))
  650.                                                 return false;
  651.                                 }
  652.                         }
  653.                 } else {
  654.                         /* Border made up from two parts and can't be plotted
  655.                          * with rectangles */
  656.                         z[0] = p[0];
  657.                         z[1] = p[1];
  658.                         z[2] = (p[0] + p[2]) / 2;
  659.                         z[3] = (p[1] + p[3]) / 2;
  660.                         z[4] = (p[6] + p[4]) / 2;
  661.                         z[5] = (p[7] + p[5]) / 2;
  662.                         z[6] = p[6];
  663.                         z[7] = p[7];
  664.                         if (!plot->polygon(z, 4, plot_style_bdr_in))
  665.                                 return false;
  666.                         z[0] = p[2];
  667.                         z[1] = p[3];
  668.                         z[6] = p[4];
  669.                         z[7] = p[5];
  670.                         if (!plot->polygon(z, 4, plot_style_bdr_out))
  671.                                 return false;
  672.                 }
  673.                 break;
  674.         }
  675.  
  676.         return true;
  677. }
  678.  
  679.  
  680. /**
  681.  * Draw borders for a box.
  682.  *
  683.  * \param  box          box to draw
  684.  * \param  x_parent     coordinate of left padding edge of parent of box
  685.  * \param  y_parent     coordinate of top padding edge of parent of box
  686.  * \param  p_width      width of padding box
  687.  * \param  p_height     height of padding box
  688.  * \param  scale        scale for redraw
  689.  * \param  ctx          current redraw context
  690.  * \return true if successful, false otherwise
  691.  */
  692.  
  693. static bool html_redraw_borders(struct box *box, int x_parent, int y_parent,
  694.                 int p_width, int p_height, const struct rect *clip, float scale,
  695.                 const struct redraw_context *ctx)
  696. {
  697.         unsigned int sides[] = { LEFT, RIGHT, TOP, BOTTOM };
  698.         int top = box->border[TOP].width;
  699.         int right = box->border[RIGHT].width;
  700.         int bottom = box->border[BOTTOM].width;
  701.         int left = box->border[LEFT].width;
  702.         int x, y;
  703.         unsigned int i, side;
  704.         int p[8]; /* Box border vertices */
  705.         int z[8]; /* Border vertices */
  706.         bool square_end_1 = false;
  707.         bool square_end_2 = false;
  708.  
  709.         x = x_parent + box->x;
  710.         y = y_parent + box->y;
  711.  
  712.         if (scale != 1.0) {
  713.                 top *= scale;
  714.                 right *= scale;
  715.                 bottom *= scale;
  716.                 left *= scale;
  717.                 x *= scale;
  718.                 y *= scale;
  719.         }
  720.  
  721.         assert(box->style);
  722.  
  723.         /* Calculate border vertices
  724.          *
  725.          *    A----------------------+
  726.          *    | \                  / |
  727.          *    |   B--------------+   |
  728.          *    |   |              |   |
  729.          *    |   +--------------C   |
  730.          *    | /                  \ |
  731.          *    +----------------------D
  732.          */
  733.         p[0] = x - left;                p[1] = y - top;                 /* A */
  734.         p[2] = x;                       p[3] = y;                       /* B */
  735.         p[4] = x + p_width;             p[5] = y + p_height;            /* C */
  736.         p[6] = x + p_width + right;     p[7] = y + p_height + bottom;   /* D */
  737.  
  738.         for (i = 0; i != 4; i++) {
  739.                 colour col = 0;
  740.                 side = sides[i]; /* plot order */
  741.  
  742.                 if (box->border[side].width == 0 ||
  743.                                 nscss_color_is_transparent(box->border[side].c))
  744.                         continue;
  745.  
  746.                 switch (side) {
  747.                 case LEFT:
  748.                         square_end_1 = (top == 0);
  749.                         square_end_2 = (bottom == 0);
  750.  
  751.                         z[0] = p[0];    z[1] = p[7];
  752.                         z[2] = p[2];    z[3] = p[5];
  753.                         z[4] = p[2];    z[5] = p[3];
  754.                         z[6] = p[0];    z[7] = p[1];
  755.  
  756.                         if (nscss_color_is_transparent(box->border[TOP].c) ==
  757.                                         false &&
  758.                                         box->border[TOP].style !=
  759.                                         CSS_BORDER_STYLE_DOUBLE) {
  760.                                 /* make border overhang top corner fully,
  761.                                  * if top border is opaque */
  762.                                 z[5] -= top;
  763.                                 square_end_1 = true;
  764.                         }
  765.                         if (nscss_color_is_transparent(box->border[BOTTOM].c) ==
  766.                                         false &&
  767.                                         box->border[BOTTOM].style !=
  768.                                         CSS_BORDER_STYLE_DOUBLE) {
  769.                                 /* make border overhang bottom corner fully,
  770.                                  * if bottom border is opaque */
  771.                                 z[3] += bottom;
  772.                                 square_end_2 = true;
  773.                         }
  774.  
  775.                         col = nscss_color_to_ns(box->border[side].c);
  776.  
  777.                         if (!html_redraw_border_plot(side, z, col,
  778.                                         box->border[side].style,
  779.                                         box->border[side].width * scale,
  780.                                         square_end_1 && square_end_2,
  781.                                         clip, ctx))
  782.                                 return false;
  783.                         break;
  784.                 case RIGHT:
  785.                         square_end_1 = (top == 0);
  786.                         square_end_2 = (bottom == 0);
  787.  
  788.                         z[0] = p[6];    z[1] = p[1];
  789.                         z[2] = p[4];    z[3] = p[3];
  790.                         z[4] = p[4];    z[5] = p[5];
  791.                         z[6] = p[6];    z[7] = p[7];
  792.  
  793.                         if (nscss_color_is_transparent(box->border[TOP].c) ==
  794.                                         false &&
  795.                                         box->border[TOP].style !=
  796.                                         CSS_BORDER_STYLE_DOUBLE) {
  797.                                 /* make border overhang top corner fully,
  798.                                  * if top border is opaque */
  799.                                 z[3] -= top;
  800.                                 square_end_1 = true;
  801.                         }
  802.                         if (nscss_color_is_transparent(box->border[BOTTOM].c) ==
  803.                                         false &&
  804.                                         box->border[BOTTOM].style !=
  805.                                         CSS_BORDER_STYLE_DOUBLE) {
  806.                                 /* make border overhang bottom corner fully,
  807.                                  * if bottom border is opaque */
  808.                                 z[5] += bottom;
  809.                                 square_end_2 = true;
  810.                         }
  811.  
  812.                         col = nscss_color_to_ns(box->border[side].c);
  813.  
  814.                         if (!html_redraw_border_plot(side, z, col,
  815.                                         box->border[side].style,
  816.                                         box->border[side].width * scale,
  817.                                         square_end_1 && square_end_2,
  818.                                         clip, ctx))
  819.                                 return false;
  820.                         break;
  821.                 case TOP:
  822.                         if (clip->y0 > p[3])
  823.                                 /* clip rectangle is below border; nothing to
  824.                                  * plot */
  825.                                 continue;
  826.  
  827.                         square_end_1 = (left == 0);
  828.                         square_end_2 = (right == 0);
  829.  
  830.                         z[0] = p[2];    z[1] = p[3];
  831.                         z[2] = p[0];    z[3] = p[1];
  832.                         z[4] = p[6];    z[5] = p[1];
  833.                         z[6] = p[4];    z[7] = p[3];
  834.  
  835.                         if (box->border[TOP].style ==
  836.                                         CSS_BORDER_STYLE_SOLID &&
  837.                                         box->border[TOP].c ==
  838.                                         box->border[LEFT].c) {
  839.                                 /* don't bother overlapping left corner if
  840.                                  * it's the same colour anyway */
  841.                                 z[2] += left;
  842.                                 square_end_1 = true;
  843.                         }
  844.                         if (box->border[TOP].style ==
  845.                                         CSS_BORDER_STYLE_SOLID &&
  846.                                         box->border[TOP].c ==
  847.                                         box->border[RIGHT].c) {
  848.                                 /* don't bother overlapping right corner if
  849.                                  * it's the same colour anyway */
  850.                                 z[4] -= right;
  851.                                 square_end_2 = true;
  852.                         }
  853.  
  854.                         col = nscss_color_to_ns(box->border[side].c);
  855.  
  856.                         if (!html_redraw_border_plot(side, z, col,
  857.                                         box->border[side].style,
  858.                                         box->border[side].width * scale,
  859.                                         square_end_1 && square_end_2,
  860.                                         clip, ctx))
  861.                                 return false;
  862.                         break;
  863.                 case BOTTOM:
  864.                         if (clip->y1 < p[5])
  865.                                 /* clip rectangle is above border; nothing to
  866.                                  * plot */
  867.                                 continue;
  868.  
  869.                         square_end_1 = (left == 0);
  870.                         square_end_2 = (right == 0);
  871.  
  872.                         z[0] = p[4];    z[1] = p[5];
  873.                         z[2] = p[6];    z[3] = p[7];
  874.                         z[4] = p[0];    z[5] = p[7];
  875.                         z[6] = p[2];    z[7] = p[5];
  876.  
  877.                         if (box->border[BOTTOM].style ==
  878.                                         CSS_BORDER_STYLE_SOLID &&
  879.                                         box->border[BOTTOM].c ==
  880.                                         box->border[LEFT].c) {
  881.                                 /* don't bother overlapping left corner if
  882.                                  * it's the same colour anyway */
  883.                                 z[4] += left;
  884.                                 square_end_1 = true;
  885.                         }
  886.                         if (box->border[BOTTOM].style ==
  887.                                         CSS_BORDER_STYLE_SOLID &&
  888.                                         box->border[BOTTOM].c ==
  889.                                         box->border[RIGHT].c) {
  890.                                 /* don't bother overlapping right corner if
  891.                                  * it's the same colour anyway */
  892.                                 z[2] -= right;
  893.                                 square_end_2 = true;
  894.                         }
  895.  
  896.                         col = nscss_color_to_ns(box->border[side].c);
  897.  
  898.                         if (!html_redraw_border_plot(side, z, col,
  899.                                         box->border[side].style,
  900.                                         box->border[side].width * scale,
  901.                                         square_end_1 && square_end_2,
  902.                                         clip, ctx))
  903.                                 return false;
  904.                         break;
  905.                 default:
  906.                         assert(side == TOP || side == BOTTOM ||
  907.                                         side == LEFT || side == RIGHT);
  908.                         break;
  909.                 }
  910.         }
  911.  
  912.         return true;
  913. }
  914.  
  915.  
  916. /**
  917.  * Draw an inline's borders.
  918.  *
  919.  * \param  box    BOX_INLINE which created the border
  920.  * \param  b      coordinates of border edge rectangle
  921.  * \param  scale  scale for redraw
  922.  * \param  first  true if this is the first rectangle associated with the inline
  923.  * \param  last   true if this is the last rectangle associated with the inline
  924.  * \param  ctx    current redraw context
  925.  * \return true if successful, false otherwise
  926.  */
  927.  
  928. static bool html_redraw_inline_borders(struct box *box, struct rect b,
  929.                 const struct rect *clip, float scale, bool first, bool last,
  930.                 const struct redraw_context *ctx)
  931. {
  932.         int top = box->border[TOP].width;
  933.         int right = box->border[RIGHT].width;
  934.         int bottom = box->border[BOTTOM].width;
  935.         int left = box->border[LEFT].width;
  936.         colour col;
  937.         int p[8]; /* Box border vertices */
  938.         int z[8]; /* Border vertices */
  939.         bool square_end_1;
  940.         bool square_end_2;
  941.  
  942.         if (scale != 1.0) {
  943.                 top *= scale;
  944.                 right *= scale;
  945.                 bottom *= scale;
  946.                 left *= scale;
  947.         }
  948.  
  949.         /* Calculate border vertices
  950.          *
  951.          *    A----------------------+
  952.          *    | \                  / |
  953.          *    |   B--------------+   |
  954.          *    |   |              |   |
  955.          *    |   +--------------C   |
  956.          *    | /                  \ |
  957.          *    +----------------------D
  958.          */
  959.         p[0] = b.x0;                            p[1] = b.y0;            /* A */
  960.         p[2] = first ? b.x0 + left : b.x0;      p[3] = b.y0 + top;      /* B */
  961.         p[4] = last ? b.x1 - right : b.x1;      p[5] = b.y1 - bottom;   /* C */
  962.         p[6] = b.x1;                            p[7] = b.y1;            /* D */
  963.  
  964.         assert(box->style);
  965.  
  966.         /* Left */
  967.         square_end_1 = (top == 0);
  968.         square_end_2 = (bottom == 0);
  969.         if (left != 0 && first && nscss_color_is_transparent(
  970.                         box->border[LEFT].c) == false) {
  971.                 col = nscss_color_to_ns(box->border[LEFT].c);
  972.  
  973.                 z[0] = p[0];    z[1] = p[7];
  974.                 z[2] = p[2];    z[3] = p[5];
  975.                 z[4] = p[2];    z[5] = p[3];
  976.                 z[6] = p[0];    z[7] = p[1];
  977.  
  978.                 if (nscss_color_is_transparent(box->border[TOP].c) == false &&
  979.                                 box->border[TOP].style !=
  980.                                 CSS_BORDER_STYLE_DOUBLE) {
  981.                         /* make border overhang top corner fully,
  982.                          * if top border is opaque */
  983.                         z[5] -= top;
  984.                         square_end_1 = true;
  985.                 }
  986.  
  987.                 if (nscss_color_is_transparent(box->border[BOTTOM].c) ==
  988.                                 false &&
  989.                                 box->border[BOTTOM].style !=
  990.                                 CSS_BORDER_STYLE_DOUBLE) {
  991.                         /* make border overhang bottom corner fully,
  992.                          * if bottom border is opaque */
  993.                         z[3] += bottom;
  994.                         square_end_2 = true;
  995.                 }
  996.  
  997.                 if (!html_redraw_border_plot(LEFT, z, col,
  998.                                 box->border[LEFT].style,
  999.                                 left, square_end_1 && square_end_2,
  1000.                                 clip, ctx))
  1001.                         return false;
  1002.         }
  1003.  
  1004.         /* Right */
  1005.         square_end_1 = (top == 0);
  1006.         square_end_2 = (bottom == 0);
  1007.         if (right != 0 && last && nscss_color_is_transparent(
  1008.                         box->border[RIGHT].c) == false) {
  1009.                 col = nscss_color_to_ns(box->border[RIGHT].c);
  1010.  
  1011.                 z[0] = p[6];    z[1] = p[1];
  1012.                 z[2] = p[4];    z[3] = p[3];
  1013.                 z[4] = p[4];    z[5] = p[5];
  1014.                 z[6] = p[6];    z[7] = p[7];
  1015.  
  1016.                 if (nscss_color_is_transparent(box->border[TOP].c) == false &&
  1017.                                 box->border[TOP].style !=
  1018.                                 CSS_BORDER_STYLE_DOUBLE) {
  1019.                         /* make border overhang top corner fully,
  1020.                          * if top border is opaque */
  1021.                         z[3] -= top;
  1022.                         square_end_1 = true;
  1023.                 }
  1024.  
  1025.                 if (nscss_color_is_transparent(box->border[BOTTOM].c) ==
  1026.                                 false &&
  1027.                                 box->border[BOTTOM].style !=
  1028.                                 CSS_BORDER_STYLE_DOUBLE) {
  1029.                         /* make border overhang bottom corner fully,
  1030.                          * if bottom border is opaque */
  1031.                         z[5] += bottom;
  1032.                         square_end_2 = true;
  1033.                 }
  1034.  
  1035.                 if (!html_redraw_border_plot(RIGHT, z, col,
  1036.                                 box->border[RIGHT].style,
  1037.                                 right, square_end_1 && square_end_2,
  1038.                                 clip, ctx))
  1039.                         return false;
  1040.         }
  1041.  
  1042.         /* Top */
  1043.         square_end_1 = (left == 0);
  1044.         square_end_2 = (right == 0);
  1045.         if (top != 0 && nscss_color_is_transparent(
  1046.                         box->border[TOP].c) == false) {
  1047.                 col = nscss_color_to_ns(box->border[TOP].c);
  1048.  
  1049.                 z[0] = p[2];    z[1] = p[3];
  1050.                 z[2] = p[0];    z[3] = p[1];
  1051.                 z[4] = p[6];    z[5] = p[1];
  1052.                 z[6] = p[4];    z[7] = p[3];
  1053.  
  1054.                 if (first && box->border[TOP].style ==
  1055.                                 CSS_BORDER_STYLE_SOLID &&
  1056.                                 box->border[TOP].c ==
  1057.                                 box->border[LEFT].c) {
  1058.                         /* don't bother overlapping left corner if
  1059.                          * it's the same colour anyway */
  1060.                         z[2] += left;
  1061.                         square_end_1 = true;
  1062.                 }
  1063.  
  1064.                 if (last && box->border[TOP].style ==
  1065.                                 CSS_BORDER_STYLE_SOLID &&
  1066.                                 box->border[TOP].c ==
  1067.                                 box->border[RIGHT].c) {
  1068.                         /* don't bother overlapping right corner if
  1069.                          * it's the same colour anyway */
  1070.                         z[4] -= right;
  1071.                         square_end_2 = true;
  1072.                 }
  1073.  
  1074.                 if (!html_redraw_border_plot(TOP, z, col,
  1075.                                 box->border[TOP].style,
  1076.                                 top, square_end_1 && square_end_2,
  1077.                                 clip, ctx))
  1078.                         return false;
  1079.         }
  1080.  
  1081.         /* Bottom */
  1082.         square_end_1 = (left == 0);
  1083.         square_end_2 = (right == 0);
  1084.         if (bottom != 0 && nscss_color_is_transparent(
  1085.                         box->border[BOTTOM].c) == false) {
  1086.                 col = nscss_color_to_ns(box->border[BOTTOM].c);
  1087.  
  1088.                 z[0] = p[4];    z[1] = p[5];
  1089.                 z[2] = p[6];    z[3] = p[7];
  1090.                 z[4] = p[0];    z[5] = p[7];
  1091.                 z[6] = p[2];    z[7] = p[5];
  1092.  
  1093.                 if (first && box->border[BOTTOM].style ==
  1094.                                 CSS_BORDER_STYLE_SOLID &&
  1095.                                 box->border[BOTTOM].c ==
  1096.                                 box->border[LEFT].c) {
  1097.                         /* don't bother overlapping left corner if
  1098.                          * it's the same colour anyway */
  1099.                         z[4] += left;
  1100.                         square_end_1 = true;
  1101.                 }
  1102.  
  1103.                 if (last && box->border[BOTTOM].style ==
  1104.                                 CSS_BORDER_STYLE_SOLID &&
  1105.                                 box->border[BOTTOM].c ==
  1106.                                 box->border[RIGHT].c) {
  1107.                         /* don't bother overlapping right corner if
  1108.                          * it's the same colour anyway */
  1109.                         z[2] -= right;
  1110.                         square_end_2 = true;
  1111.                 }
  1112.  
  1113.                 if (!html_redraw_border_plot(BOTTOM, z, col,
  1114.                                 box->border[BOTTOM].style,
  1115.                                 bottom, square_end_1 && square_end_2,
  1116.                                 clip, ctx))
  1117.                         return false;
  1118.         }
  1119.  
  1120.         return true;
  1121. }
  1122.  
  1123.  
  1124. /**
  1125.  * Plot a checkbox.
  1126.  *
  1127.  * \param  x         left coordinate
  1128.  * \param  y         top coordinate
  1129.  * \param  width     dimensions of checkbox
  1130.  * \param  height    dimensions of checkbox
  1131.  * \param  selected  the checkbox is selected
  1132.  * \param  ctx       current redraw context
  1133.  * \return true if successful, false otherwise
  1134.  */
  1135.  
  1136. static bool html_redraw_checkbox(int x, int y, int width, int height,
  1137.                 bool selected, const struct redraw_context *ctx)
  1138. {
  1139.         const struct plotter_table *plot = ctx->plot;
  1140.         double z = width * 0.15;
  1141.         if (z == 0)
  1142.                 z = 1;
  1143.  
  1144.         if (!(plot->rectangle(x, y, x + width, y + height,
  1145.                         plot_style_fill_wbasec) &&
  1146.                 plot->line(x, y, x + width, y, plot_style_stroke_darkwbasec) &&
  1147.                 plot->line(x, y, x, y + height, plot_style_stroke_darkwbasec) &&
  1148.                 plot->line(x + width, y, x + width, y + height,
  1149.                                 plot_style_stroke_lightwbasec) &&
  1150.                 plot->line(x, y + height, x + width, y + height,
  1151.                                 plot_style_stroke_lightwbasec)))
  1152.                 return false;
  1153.  
  1154.         if (selected) {
  1155.                 if (width < 12 || height < 12) {
  1156.                         /* render a solid box instead of a tick */
  1157.                         if (!plot->rectangle(x + z + z, y + z + z,
  1158.                                         x + width - z, y + height - z,
  1159.                                         plot_style_fill_wblobc))
  1160.                                 return false;
  1161.                 } else {
  1162.                         /* render a tick, as it'll fit comfortably */
  1163.                         if (!(plot->line(x + width - z,
  1164.                                         y + z,
  1165.                                         x + (z * 3),
  1166.                                         y + height - z,
  1167.                                         plot_style_stroke_wblobc) &&
  1168.  
  1169.                                 plot->line(x + (z * 3),
  1170.                                         y + height - z,
  1171.                                         x + z + z,
  1172.                                         y + (height / 2),
  1173.                                         plot_style_stroke_wblobc)))
  1174.                                 return false;
  1175.                 }
  1176.         }
  1177.         return true;
  1178. }
  1179.  
  1180.  
  1181. /**
  1182.  * Plot a radio icon.
  1183.  *
  1184.  * \param  x         left coordinate
  1185.  * \param  y         top coordinate
  1186.  * \param  width     dimensions of radio icon
  1187.  * \param  height    dimensions of radio icon
  1188.  * \param  selected  the radio icon is selected
  1189.  * \param  ctx       current redraw context
  1190.  * \return true if successful, false otherwise
  1191.  */
  1192. static bool html_redraw_radio(int x, int y, int width, int height,
  1193.                 bool selected, const struct redraw_context *ctx)
  1194. {
  1195.         const struct plotter_table *plot = ctx->plot;
  1196.  
  1197.         /* plot background of radio button */
  1198.         if (!plot->disc(x + width * 0.5,
  1199.                         y + height * 0.5,
  1200.                         width * 0.5 - 1,
  1201.                         plot_style_fill_wbasec))
  1202.                 return false;
  1203.  
  1204.         /* plot dark arc */
  1205.         if (!plot->arc(x + width * 0.5,
  1206.                         y + height * 0.5,
  1207.                         width * 0.5 - 1,
  1208.                         45,
  1209.                         225,
  1210.                         plot_style_fill_darkwbasec))
  1211.                 return false;
  1212.  
  1213.         /* plot light arc */
  1214.         if (!plot->arc(x + width * 0.5,
  1215.                         y + height * 0.5,
  1216.                         width * 0.5 - 1,
  1217.                         225,
  1218.                         45,
  1219.                         plot_style_fill_lightwbasec))
  1220.                 return false;
  1221.  
  1222.         if (selected) {
  1223.                 /* plot selection blob */
  1224.                 if (!plot->disc(x + width * 0.5,
  1225.                                 y + height * 0.5,
  1226.                                 width * 0.3 - 1,
  1227.                                 plot_style_fill_wblobc))
  1228.                         return false;
  1229.         }
  1230.  
  1231.         return true;
  1232. }
  1233.  
  1234.  
  1235. /**
  1236.  * Plot a file upload input.
  1237.  *
  1238.  * \param  x         left coordinate
  1239.  * \param  y         top coordinate
  1240.  * \param  width     dimensions of input
  1241.  * \param  height    dimensions of input
  1242.  * \param  box       box of input
  1243.  * \param  scale     scale for redraw
  1244.  * \param  background_colour  current background colour
  1245.  * \param  ctx       current redraw context
  1246.  * \return true if successful, false otherwise
  1247.  */
  1248.  
  1249. static bool html_redraw_file(int x, int y, int width, int height,
  1250.                 struct box *box, float scale, colour background_colour,
  1251.                 const struct redraw_context *ctx)
  1252. {
  1253.         int text_width;
  1254.         const char *text;
  1255.         size_t length;
  1256.         plot_font_style_t fstyle;
  1257.  
  1258.         font_plot_style_from_css(box->style, &fstyle);
  1259.         fstyle.background = background_colour;
  1260.  
  1261.         if (box->gadget->value)
  1262.                 text = box->gadget->value;
  1263.         else
  1264.                 text = messages_get("Form_Drop");
  1265.         length = strlen(text);
  1266.  
  1267.         if (!nsfont.font_width(&fstyle, text, length, &text_width))
  1268.                 return false;
  1269.         text_width *= scale;
  1270.         if (width < text_width + 8)
  1271.                 x = x + width - text_width - 4;
  1272.         else
  1273.                 x = x + 4;
  1274.  
  1275.         return ctx->plot->text(x, y + height * 0.75, text, length, &fstyle);
  1276. }
  1277.  
  1278.  
  1279. /**
  1280.  * Plot background images.
  1281.  *
  1282.  * \param  x      coordinate of box
  1283.  * \param  y      coordinate of box
  1284.  * \param  box    box to draw background image of
  1285.  * \param  scale  scale for redraw
  1286.  * \param  clip   current clip rectangle
  1287.  * \param  background_colour  current background colour
  1288.  * \param  background  box containing background details (usually ::box)
  1289.  * \param  ctx    current redraw context
  1290.  * \return true if successful, false otherwise
  1291.  *
  1292.  * The reason for the presence of ::background is the backwards compatibility
  1293.  * mess that is backgrounds on <body>. The background will be drawn relative
  1294.  * to ::box, using the background information contained within ::background.
  1295.  */
  1296.  
  1297. static bool html_redraw_background(int x, int y, struct box *box, float scale,
  1298.                 const struct rect *clip, colour *background_colour,
  1299.                 struct box *background, const struct redraw_context *ctx)
  1300. {
  1301.         const struct plotter_table *plot = ctx->plot;
  1302.         bool repeat_x = false;
  1303.         bool repeat_y = false;
  1304.         bool plot_colour = true;
  1305.         bool plot_content;
  1306.         bool clip_to_children = false;
  1307.         struct box *clip_box = box;
  1308.         int ox = x, oy = y;
  1309.         int width, height;
  1310.         css_fixed hpos = 0, vpos = 0;
  1311.         css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX;
  1312.         struct box *parent;
  1313.         struct rect r = *clip;
  1314.         css_color bgcol;
  1315.         plot_style_t pstyle_fill_bg = {
  1316.                 .fill_type = PLOT_OP_TYPE_SOLID,
  1317.                 .fill_colour = *background_colour,
  1318.         };
  1319.  
  1320.         if (ctx->background_images == false)
  1321.                 return true;
  1322.  
  1323.         plot_content = (background->background != NULL);
  1324.  
  1325.         if (plot_content) {
  1326.                 if (!box->parent) {
  1327.                         /* Root element, special case:
  1328.                          * background origin calc. is based on margin box */
  1329.                         x -= box->margin[LEFT] * scale;
  1330.                         y -= box->margin[TOP] * scale;
  1331.                         width = box->margin[LEFT] + box->padding[LEFT] +
  1332.                                         box->width + box->padding[RIGHT] +
  1333.                                         box->margin[RIGHT];
  1334.                         height = box->margin[TOP] + box->padding[TOP] +
  1335.                                         box->height + box->padding[BOTTOM] +
  1336.                                         box->margin[BOTTOM];
  1337.                 } else {
  1338.                         width = box->padding[LEFT] + box->width +
  1339.                                         box->padding[RIGHT];
  1340.                         height = box->padding[TOP] + box->height +
  1341.                                         box->padding[BOTTOM];
  1342.                 }
  1343.                 /* handle background-repeat */
  1344.                 switch (css_computed_background_repeat(background->style)) {
  1345.                 case CSS_BACKGROUND_REPEAT_REPEAT:
  1346.                         repeat_x = repeat_y = true;
  1347.                         /* optimisation: only plot the colour if
  1348.                          * bitmap is not opaque */
  1349.                         plot_colour = !content_get_opaque(background->background);
  1350.                         break;
  1351.  
  1352.                 case CSS_BACKGROUND_REPEAT_REPEAT_X:
  1353.                         repeat_x = true;
  1354.                         break;
  1355.  
  1356.                 case CSS_BACKGROUND_REPEAT_REPEAT_Y:
  1357.                         repeat_y = true;
  1358.                         break;
  1359.  
  1360.                 case CSS_BACKGROUND_REPEAT_NO_REPEAT:
  1361.                         break;
  1362.  
  1363.                 default:
  1364.                         break;
  1365.                 }
  1366.  
  1367.                 /* handle background-position */
  1368.                 css_computed_background_position(background->style,
  1369.                                 &hpos, &hunit, &vpos, &vunit);
  1370.                 if (hunit == CSS_UNIT_PCT) {
  1371.                         x += (width -
  1372.                                 content_get_width(background->background)) *
  1373.                                 scale * FIXTOFLT(hpos) / 100.;
  1374.                 } else {
  1375.                         x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit,
  1376.                                         background->style)) * scale);
  1377.                 }
  1378.  
  1379.                 if (vunit == CSS_UNIT_PCT) {
  1380.                         y += (height -
  1381.                                 content_get_height(background->background)) *
  1382.                                 scale * FIXTOFLT(vpos) / 100.;
  1383.                 } else {
  1384.                         y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit,
  1385.                                         background->style)) * scale);
  1386.                 }
  1387.         }
  1388.  
  1389.         /* special case for table rows as their background needs
  1390.          * to be clipped to all the cells */
  1391.         if (box->type == BOX_TABLE_ROW) {
  1392.                 css_fixed h = 0, v = 0;
  1393.                 css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX;
  1394.  
  1395.                 for (parent = box->parent;
  1396.                         ((parent) && (parent->type != BOX_TABLE));
  1397.                                 parent = parent->parent);
  1398.                 assert(parent && (parent->style));
  1399.  
  1400.                 css_computed_border_spacing(parent->style, &h, &hu, &v, &vu);
  1401.  
  1402.                 clip_to_children = (h > 0) || (v > 0);
  1403.  
  1404.                 if (clip_to_children)
  1405.                         clip_box = box->children;
  1406.         }
  1407.  
  1408.         for (; clip_box; clip_box = clip_box->next) {
  1409.                 /* clip to child boxes if needed */
  1410.                 if (clip_to_children) {
  1411.                         assert(clip_box->type == BOX_TABLE_CELL);
  1412.  
  1413.                         /* update clip.* to the child cell */
  1414.                         r.x0 = ox + (clip_box->x * scale);
  1415.                         r.y0 = oy + (clip_box->y * scale);
  1416.                         r.x1 = r.x0 + (clip_box->padding[LEFT] +
  1417.                                         clip_box->width +
  1418.                                         clip_box->padding[RIGHT]) * scale;
  1419.                         r.y1 = r.y0 + (clip_box->padding[TOP] +
  1420.                                         clip_box->height +
  1421.                                         clip_box->padding[BOTTOM]) * scale;
  1422.  
  1423.                         if (r.x0 < clip->x0) r.x0 = clip->x0;
  1424.                         if (r.y0 < clip->y0) r.y0 = clip->y0;
  1425.                         if (r.x1 > clip->x1) r.x1 = clip->x1;
  1426.                         if (r.y1 > clip->y1) r.y1 = clip->y1;
  1427.  
  1428.                         css_computed_background_color(clip_box->style, &bgcol);
  1429.  
  1430.                         /* <td> attributes override <tr> */
  1431.                         /* if the background content is opaque there
  1432.                          * is no need to plot underneath it.
  1433.                          */
  1434.                         if ((r.x0 >= r.x1) ||
  1435.                             (r.y0 >= r.y1) ||
  1436.                             (nscss_color_is_transparent(bgcol) == false) ||
  1437.                             ((clip_box->background != NULL) &&
  1438.                              content_get_opaque(clip_box->background)))
  1439.                                 continue;
  1440.                 }
  1441.  
  1442.                 /* plot the background colour */
  1443.                 css_computed_background_color(background->style, &bgcol);
  1444.  
  1445.                 if (nscss_color_is_transparent(bgcol) == false) {
  1446.                         *background_colour = nscss_color_to_ns(bgcol);
  1447.                         pstyle_fill_bg.fill_colour = *background_colour;
  1448.                         if (plot_colour)
  1449.                                 if (!plot->rectangle(r.x0, r.y0, r.x1, r.y1,
  1450.                                                 &pstyle_fill_bg))
  1451.                                         return false;
  1452.                 }
  1453.                 /* and plot the image */
  1454.                 if (plot_content) {
  1455.                         width = content_get_width(background->background);
  1456.                         height = content_get_height(background->background);
  1457.  
  1458.                         /* ensure clip area only as large as required */
  1459.                         if (!repeat_x) {
  1460.                                 if (r.x0 < x)
  1461.                                         r.x0 = x;
  1462.                                 if (r.x1 > x + width * scale)
  1463.                                         r.x1 = x + width * scale;
  1464.                         }
  1465.                         if (!repeat_y) {
  1466.                                 if (r.y0 < y)
  1467.                                         r.y0 = y;
  1468.                                 if (r.y1 > y + height * scale)
  1469.                                         r.y1 = y + height * scale;
  1470.                         }
  1471.                         /* valid clipping rectangles only */
  1472.                         if ((r.x0 < r.x1) && (r.y0 < r.y1)) {
  1473.                                 struct content_redraw_data bg_data;
  1474.  
  1475.                                 if (!plot->clip(&r))
  1476.                                         return false;
  1477.  
  1478.                                 bg_data.x = x;
  1479.                                 bg_data.y = y;
  1480.                                 bg_data.width = ceilf(width * scale);
  1481.                                 bg_data.height = ceilf(height * scale);
  1482.                                 bg_data.background_colour = *background_colour;
  1483.                                 bg_data.scale = scale;
  1484.                                 bg_data.repeat_x = repeat_x;
  1485.                                 bg_data.repeat_y = repeat_y;
  1486.  
  1487.                                 if (!content_redraw(background->background,
  1488.                                                 &bg_data, &r, ctx))
  1489.                                         return false;
  1490.                         }
  1491.                 }
  1492.  
  1493.                 /* only <tr> rows being clipped to child boxes loop */
  1494.                 if (!clip_to_children)
  1495.                         return true;
  1496.         }
  1497.         return true;
  1498. }
  1499.  
  1500.  
  1501. /**
  1502.  * Plot an inline's background and/or background image.
  1503.  *
  1504.  * \param  x      coordinate of box
  1505.  * \param  y      coordinate of box
  1506.  * \param  box    BOX_INLINE which created the background
  1507.  * \param  scale  scale for redraw
  1508.  * \param  clip   coordinates of clip rectangle
  1509.  * \param  b      coordinates of border edge rectangle
  1510.  * \param  first  true if this is the first rectangle associated with the inline
  1511.  * \param  last   true if this is the last rectangle associated with the inline
  1512.  * \param  background_colour  updated to current background colour if plotted
  1513.  * \param  ctx    current redraw context
  1514.  * \return true if successful, false otherwise
  1515.  */
  1516.  
  1517. static bool html_redraw_inline_background(int x, int y, struct box *box,
  1518.                 float scale, const struct rect *clip, struct rect b,
  1519.                 bool first, bool last, colour *background_colour,
  1520.                 const struct redraw_context *ctx)
  1521. {
  1522.         const struct plotter_table *plot = ctx->plot;
  1523.         struct rect r = *clip;
  1524.         bool repeat_x = false;
  1525.         bool repeat_y = false;
  1526.         bool plot_colour = true;
  1527.         bool plot_content;
  1528.         css_fixed hpos = 0, vpos = 0;
  1529.         css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX;
  1530.         css_color bgcol;
  1531.         plot_style_t pstyle_fill_bg = {
  1532.                 .fill_type = PLOT_OP_TYPE_SOLID,
  1533.                 .fill_colour = *background_colour,
  1534.         };
  1535.  
  1536.         plot_content = (box->background != NULL);
  1537.  
  1538.         if (html_redraw_printing && nsoption_bool(remove_backgrounds))
  1539.                 return true;
  1540.  
  1541.         if (plot_content) {
  1542.                 /* handle background-repeat */
  1543.                 switch (css_computed_background_repeat(box->style)) {
  1544.                 case CSS_BACKGROUND_REPEAT_REPEAT:
  1545.                         repeat_x = repeat_y = true;
  1546.                         /* optimisation: only plot the colour if
  1547.                          * bitmap is not opaque
  1548.                          */
  1549.                         plot_colour = !content_get_opaque(box->background);
  1550.                         break;
  1551.  
  1552.                 case CSS_BACKGROUND_REPEAT_REPEAT_X:
  1553.                         repeat_x = true;
  1554.                         break;
  1555.  
  1556.                 case CSS_BACKGROUND_REPEAT_REPEAT_Y:
  1557.                         repeat_y = true;
  1558.                         break;
  1559.  
  1560.                 case CSS_BACKGROUND_REPEAT_NO_REPEAT:
  1561.                         break;
  1562.  
  1563.                 default:
  1564.                         break;
  1565.                 }
  1566.  
  1567.                 /* handle background-position */
  1568.                 css_computed_background_position(box->style,
  1569.                                 &hpos, &hunit, &vpos, &vunit);
  1570.                 if (hunit == CSS_UNIT_PCT) {
  1571.                         x += (b.x1 - b.x0 -
  1572.                                         content_get_width(box->background) *
  1573.                                         scale) * FIXTOFLT(hpos) / 100.;
  1574.  
  1575.                         if (!repeat_x && ((hpos < 2 && !first) ||
  1576.                                         (hpos > 98 && !last))){
  1577.                                 plot_content = false;
  1578.                         }
  1579.                 } else {
  1580.                         x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit,
  1581.                                         box->style)) * scale);
  1582.                 }
  1583.  
  1584.                 if (vunit == CSS_UNIT_PCT) {
  1585.                         y += (b.y1 - b.y0 -
  1586.                                         content_get_height(box->background) *
  1587.                                         scale) * FIXTOFLT(vpos) / 100.;
  1588.                 } else {
  1589.                         y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit,
  1590.                                         box->style)) * scale);
  1591.                 }
  1592.         }
  1593.  
  1594.         /* plot the background colour */
  1595.         css_computed_background_color(box->style, &bgcol);
  1596.  
  1597.         if (nscss_color_is_transparent(bgcol) == false) {
  1598.                 *background_colour = nscss_color_to_ns(bgcol);
  1599.                 pstyle_fill_bg.fill_colour = *background_colour;
  1600.  
  1601.                 if (plot_colour)
  1602.                         if (!plot->rectangle(r.x0, r.y0, r.x1, r.y1,
  1603.                                         &pstyle_fill_bg))
  1604.                                 return false;
  1605.         }
  1606.         /* and plot the image */
  1607.         if (plot_content) {
  1608.                 int width = content_get_width(box->background);
  1609.                 int height = content_get_height(box->background);
  1610.  
  1611.                 if (!repeat_x) {
  1612.                         if (r.x0 < x)
  1613.                                 r.x0 = x;
  1614.                         if (r.x1 > x + width * scale)
  1615.                                 r.x1 = x + width * scale;
  1616.                 }
  1617.                 if (!repeat_y) {
  1618.                         if (r.y0 < y)
  1619.                                 r.y0 = y;
  1620.                         if (r.y1 > y + height * scale)
  1621.                                 r.y1 = y + height * scale;
  1622.                 }
  1623.                 /* valid clipping rectangles only */
  1624.                 if ((r.x0 < r.x1) && (r.y0 < r.y1)) {
  1625.                         struct content_redraw_data bg_data;
  1626.  
  1627.                         if (!plot->clip(&r))
  1628.                                 return false;
  1629.  
  1630.                         bg_data.x = x;
  1631.                         bg_data.y = y;
  1632.                         bg_data.width = ceilf(width * scale);
  1633.                         bg_data.height = ceilf(height * scale);
  1634.                         bg_data.background_colour = *background_colour;
  1635.                         bg_data.scale = scale;
  1636.                         bg_data.repeat_x = repeat_x;
  1637.                         bg_data.repeat_y = repeat_y;
  1638.  
  1639.                         if (!content_redraw(box->background, &bg_data, &r, ctx))
  1640.                                 return false;
  1641.                 }
  1642.         }
  1643.  
  1644.         return true;
  1645. }
  1646.  
  1647.  
  1648. /**
  1649.  * Plot text decoration for an inline box.
  1650.  *
  1651.  * \param  box     box to plot decorations for, of type BOX_INLINE
  1652.  * \param  x       x coordinate of parent of box
  1653.  * \param  y       y coordinate of parent of box
  1654.  * \param  scale   scale for redraw
  1655.  * \param  colour  colour for decorations
  1656.  * \param  ratio   position of line as a ratio of line height
  1657.  * \param  ctx     current redraw context
  1658.  * \return true if successful, false otherwise
  1659.  */
  1660.  
  1661. static bool html_redraw_text_decoration_inline(struct box *box, int x, int y,
  1662.                 float scale, colour colour, float ratio,
  1663.                 const struct redraw_context *ctx)
  1664. {
  1665.         const struct plotter_table *plot = ctx->plot;
  1666.         struct box *c;
  1667.         plot_style_t plot_style_box = {
  1668.                 .stroke_type = PLOT_OP_TYPE_SOLID,
  1669.                 .stroke_colour = colour,
  1670.         };
  1671.  
  1672.         for (c = box->next;
  1673.                         c && c != box->inline_end;
  1674.                         c = c->next) {
  1675.                 if (c->type != BOX_TEXT)
  1676.                         continue;
  1677.                 if (!plot->line((x + c->x) * scale,
  1678.                                 (y + c->y + c->height * ratio) * scale,
  1679.                                 (x + c->x + c->width) * scale,
  1680.                                 (y + c->y + c->height * ratio) * scale,
  1681.                                 &plot_style_box))
  1682.                         return false;
  1683.         }
  1684.         return true;
  1685. }
  1686.  
  1687.  
  1688. /**
  1689.  * Plot text decoration for an non-inline box.
  1690.  *
  1691.  * \param  box     box to plot decorations for, of type other than BOX_INLINE
  1692.  * \param  x       x coordinate of box
  1693.  * \param  y       y coordinate of box
  1694.  * \param  scale   scale for redraw
  1695.  * \param  colour  colour for decorations
  1696.  * \param  ratio   position of line as a ratio of line height
  1697.  * \param  ctx     current redraw context
  1698.  * \return true if successful, false otherwise
  1699.  */
  1700.  
  1701. static bool html_redraw_text_decoration_block(struct box *box, int x, int y,
  1702.                 float scale, colour colour, float ratio,
  1703.                 const struct redraw_context *ctx)
  1704. {
  1705.         const struct plotter_table *plot = ctx->plot;
  1706.         struct box *c;
  1707.         plot_style_t plot_style_box = {
  1708.                 .stroke_type = PLOT_OP_TYPE_SOLID,
  1709.                 .stroke_colour = colour,
  1710.         };
  1711.  
  1712.         /* draw through text descendants */
  1713.         for (c = box->children; c; c = c->next) {
  1714.                 if (c->type == BOX_TEXT) {
  1715.                         if (!plot->line((x + c->x) * scale,
  1716.                                         (y + c->y + c->height * ratio) * scale,
  1717.                                         (x + c->x + c->width) * scale,
  1718.                                         (y + c->y + c->height * ratio) * scale,
  1719.                                         &plot_style_box))
  1720.                                 return false;
  1721.                 } else if (c->type == BOX_INLINE_CONTAINER ||
  1722.                                 c->type == BOX_BLOCK) {
  1723.                         if (!html_redraw_text_decoration_block(c,
  1724.                                         x + c->x, y + c->y,
  1725.                                         scale, colour, ratio, ctx))
  1726.                                 return false;
  1727.                 }
  1728.         }
  1729.         return true;
  1730. }
  1731.  
  1732.  
  1733. /**
  1734.  * Plot text decoration for a box.
  1735.  *
  1736.  * \param  box       box to plot decorations for
  1737.  * \param  x_parent  x coordinate of parent of box
  1738.  * \param  y_parent  y coordinate of parent of box
  1739.  * \param  scale     scale for redraw
  1740.  * \param  background_colour  current background colour
  1741.  * \param  ctx       current redraw context
  1742.  * \return true if successful, false otherwise
  1743.  */
  1744.  
  1745. static bool html_redraw_text_decoration(struct box *box,
  1746.                 int x_parent, int y_parent, float scale,
  1747.                 colour background_colour, const struct redraw_context *ctx)
  1748. {
  1749.         static const enum css_text_decoration_e decoration[] = {
  1750.                 CSS_TEXT_DECORATION_UNDERLINE, CSS_TEXT_DECORATION_OVERLINE,
  1751.                 CSS_TEXT_DECORATION_LINE_THROUGH };
  1752.         static const float line_ratio[] = { 0.9, 0.1, 0.5 };
  1753.         colour fgcol;
  1754.         unsigned int i;
  1755.         css_color col;
  1756.  
  1757.         css_computed_color(box->style, &col);
  1758.         fgcol = nscss_color_to_ns(col);
  1759.  
  1760.         /* antialias colour for under/overline */
  1761.         if (html_redraw_printing == false)
  1762.                 fgcol = blend_colour(background_colour, fgcol);
  1763.  
  1764.         if (box->type == BOX_INLINE) {
  1765.                 if (!box->inline_end)
  1766.                         return true;
  1767.                 for (i = 0; i != NOF_ELEMENTS(decoration); i++)
  1768.                         if (css_computed_text_decoration(box->style) &
  1769.                                         decoration[i])
  1770.                                 if (!html_redraw_text_decoration_inline(box,
  1771.                                                 x_parent, y_parent, scale,
  1772.                                                 fgcol, line_ratio[i], ctx))
  1773.                                         return false;
  1774.         } else {
  1775.                 for (i = 0; i != NOF_ELEMENTS(decoration); i++)
  1776.                         if (css_computed_text_decoration(box->style) &
  1777.                                         decoration[i])
  1778.                                 if (!html_redraw_text_decoration_block(box,
  1779.                                                 x_parent + box->x,
  1780.                                                 y_parent + box->y,
  1781.                                                 scale,
  1782.                                                 fgcol, line_ratio[i], ctx))
  1783.                                         return false;
  1784.         }
  1785.  
  1786.         return true;
  1787. }
  1788.  
  1789.  
  1790. /**
  1791.  * Redraw the text content of a box, possibly partially highlighted
  1792.  * because the text has been selected, or matches a search operation.
  1793.  *
  1794.  * \param  box      box with text content
  1795.  * \param  x        x co-ord of box
  1796.  * \param  y        y co-ord of box
  1797.  * \param  clip     current clip rectangle
  1798.  * \param  scale    current scale setting (1.0 = 100%)
  1799.  * \param  current_background_color
  1800.  * \param  ctx      current redraw context
  1801.  * \return true iff successful and redraw should proceed
  1802.  */
  1803.  
  1804. static bool html_redraw_text_box(const html_content *html, struct box *box,
  1805.                 int x, int y, const struct rect *clip, float scale,
  1806.                 colour current_background_color,
  1807.                 const struct redraw_context *ctx)
  1808. {
  1809.         bool excluded = (box->object != NULL);
  1810.         plot_font_style_t fstyle;
  1811.  
  1812.         font_plot_style_from_css(box->style, &fstyle);
  1813.         fstyle.background = current_background_color;
  1814.  
  1815.         if (!text_redraw(box->text, box->length, box->byte_offset,
  1816.                         box->space, &fstyle, x, y,
  1817.                         clip, box->height, scale, excluded,
  1818.                         (struct content *)html, &html->sel,
  1819.                         html->search, ctx))
  1820.                 return false;
  1821.  
  1822.         return true;
  1823. }
  1824.  
  1825. bool html_redraw_box(const html_content *html, struct box *box,
  1826.                 int x_parent, int y_parent,
  1827.                 const struct rect *clip, float scale,
  1828.                 colour current_background_color,
  1829.                 const struct redraw_context *ctx);
  1830.  
  1831. /**
  1832.  * Draw the various children of a box.
  1833.  *
  1834.  * \param  html      html content
  1835.  * \param  box       box to draw children of
  1836.  * \param  x_parent  coordinate of parent box
  1837.  * \param  y_parent  coordinate of parent box
  1838.  * \param  clip      clip rectangle
  1839.  * \param  scale     scale for redraw
  1840.  * \param  current_background_color  background colour under this box
  1841.  * \param  ctx       current redraw context
  1842.  * \return true if successful, false otherwise
  1843.  */
  1844.  
  1845. static bool html_redraw_box_children(const html_content *html, struct box *box,
  1846.                 int x_parent, int y_parent,
  1847.                 const struct rect *clip, float scale,
  1848.                 colour current_background_color,
  1849.                 const struct redraw_context *ctx)
  1850. {
  1851.         struct box *c;
  1852.  
  1853.         for (c = box->children; c; c = c->next) {
  1854.  
  1855.                 if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT)
  1856.                         if (!html_redraw_box(html, c,
  1857.                                         x_parent + box->x -
  1858.                                         scrollbar_get_offset(box->scroll_x),
  1859.                                         y_parent + box->y -
  1860.                                         scrollbar_get_offset(box->scroll_y),
  1861.                                         clip, scale, current_background_color,
  1862.                                         ctx))
  1863.                                 return false;
  1864.         }
  1865.         for (c = box->float_children; c; c = c->next_float)
  1866.                 if (!html_redraw_box(html, c,
  1867.                                 x_parent + box->x -
  1868.                                 scrollbar_get_offset(box->scroll_x),
  1869.                                 y_parent + box->y -
  1870.                                 scrollbar_get_offset(box->scroll_y),
  1871.                                 clip, scale, current_background_color,
  1872.                                 ctx))
  1873.                         return false;
  1874.  
  1875.         return true;
  1876. }
  1877.  
  1878. /**
  1879.  * Recursively draw a box.
  1880.  *
  1881.  * \param  html      html content
  1882.  * \param  box       box to draw
  1883.  * \param  x_parent  coordinate of parent box
  1884.  * \param  y_parent  coordinate of parent box
  1885.  * \param  clip      clip rectangle
  1886.  * \param  scale     scale for redraw
  1887.  * \param  current_background_color  background colour under this box
  1888.  * \param  ctx       current redraw context
  1889.  * \return true if successful, false otherwise
  1890.  *
  1891.  * x, y, clip_[xy][01] are in target coordinates.
  1892.  */
  1893.  
  1894. bool html_redraw_box(const html_content *html, struct box *box,
  1895.                 int x_parent, int y_parent,
  1896.                 const struct rect *clip, const float scale,
  1897.                 colour current_background_color,
  1898.                 const struct redraw_context *ctx)
  1899. {
  1900.         const struct plotter_table *plot = ctx->plot;
  1901.         int x, y;
  1902.         int width, height;
  1903.         int padding_left, padding_top, padding_width, padding_height;
  1904.         int border_left, border_top, border_right, border_bottom;
  1905.         struct rect r;
  1906.         int x_scrolled, y_scrolled;
  1907.         struct box *bg_box = NULL;
  1908.         bool has_x_scroll, has_y_scroll;
  1909.         css_computed_clip_rect css_rect;
  1910.  
  1911.         if (html_redraw_printing && (box->flags & PRINTED))
  1912.                 return true;
  1913.  
  1914.         /* avoid trivial FP maths */
  1915.         if (scale == 1.0) {
  1916.                 x = x_parent + box->x;
  1917.                 y = y_parent + box->y;
  1918.                 width = box->width;
  1919.                 height = box->height;
  1920.                 padding_left = box->padding[LEFT];
  1921.                 padding_top = box->padding[TOP];
  1922.                 padding_width = padding_left + box->width + box->padding[RIGHT];
  1923.                 padding_height = padding_top + box->height +
  1924.                                 box->padding[BOTTOM];
  1925.                 border_left = box->border[LEFT].width;
  1926.                 border_top = box->border[TOP].width;
  1927.                 border_right = box->border[RIGHT].width;
  1928.                 border_bottom = box->border[BOTTOM].width;
  1929.         } else {
  1930.                 x = (x_parent + box->x) * scale;
  1931.                 y = (y_parent + box->y) * scale;
  1932.                 width = box->width * scale;
  1933.                 height = box->height * scale;
  1934.                 /* left and top padding values are normally zero,
  1935.                  * so avoid trivial FP maths */
  1936.                 padding_left = box->padding[LEFT] ? box->padding[LEFT] * scale
  1937.                                 : 0;
  1938.                 padding_top = box->padding[TOP] ? box->padding[TOP] * scale
  1939.                                 : 0;
  1940.                 padding_width = (box->padding[LEFT] + box->width +
  1941.                                 box->padding[RIGHT]) * scale;
  1942.                 padding_height = (box->padding[TOP] + box->height +
  1943.                                 box->padding[BOTTOM]) * scale;
  1944.                 border_left = box->border[LEFT].width * scale;
  1945.                 border_top = box->border[TOP].width * scale;
  1946.                 border_right = box->border[RIGHT].width * scale;
  1947.                 border_bottom = box->border[BOTTOM].width * scale;
  1948.         }
  1949.  
  1950.         /* calculate rectangle covering this box and descendants */
  1951.         if (box->style && css_computed_overflow(box->style) !=
  1952.                         CSS_OVERFLOW_VISIBLE) {
  1953.                 /* box contents clipped to box size */
  1954.                 r.x0 = x - border_left;
  1955.                 r.y0 = y - border_top;
  1956.                 r.x1 = x + padding_width + border_right;
  1957.                 r.y1 = y + padding_height + border_bottom;
  1958.         } else {
  1959.                 /* box contents can hang out of the box; use descendant box */
  1960.                 if (scale == 1.0) {
  1961.                         r.x0 = x + box->descendant_x0;
  1962.                         r.y0 = y + box->descendant_y0;
  1963.                         r.x1 = x + box->descendant_x1 + 1;
  1964.                         r.y1 = y + box->descendant_y1 + 1;
  1965.                 } else {
  1966.                         r.x0 = x + box->descendant_x0 * scale;
  1967.                         r.y0 = y + box->descendant_y0 * scale;
  1968.                         r.x1 = x + box->descendant_x1 * scale + 1;
  1969.                         r.y1 = y + box->descendant_y1 * scale + 1;
  1970.                 }
  1971.                 if (!box->parent) {
  1972.                         /* root element */
  1973.                         int margin_left, margin_right;
  1974.                         int margin_top, margin_bottom;
  1975.                         if (scale == 1.0) {
  1976.                                 margin_left = box->margin[LEFT];
  1977.                                 margin_top = box->margin[TOP];
  1978.                                 margin_right = box->margin[RIGHT];
  1979.                                 margin_bottom = box->margin[BOTTOM];
  1980.                         } else {
  1981.                                 margin_left = box->margin[LEFT] * scale;
  1982.                                 margin_top = box->margin[TOP] * scale;
  1983.                                 margin_right = box->margin[RIGHT] * scale;
  1984.                                 margin_bottom = box->margin[BOTTOM] * scale;
  1985.                         }
  1986.                         r.x0 = x - border_left - margin_left < r.x0 ?
  1987.                                         x - border_left - margin_left : r.x0;
  1988.                         r.y0 = y - border_top - margin_top < r.y0 ?
  1989.                                         y - border_top - margin_top : r.y0;
  1990.                         r.x1 = x + padding_width + border_right +
  1991.                                         margin_right > r.x1 ?
  1992.                                         x + padding_width + border_right +
  1993.                                         margin_right : r.x1;
  1994.                         r.y1 = y + padding_height + border_bottom +
  1995.                                         margin_bottom > r.y1 ?
  1996.                                         y + padding_height + border_bottom +
  1997.                                         margin_bottom : r.y1;
  1998.                 }
  1999.         }
  2000.  
  2001.         /* return if the rectangle is completely outside the clip rectangle */
  2002.         if (clip->y1 < r.y0 || r.y1 < clip->y0 ||
  2003.                         clip->x1 < r.x0 || r.x1 < clip->x0)
  2004.                 return true;
  2005.  
  2006.         /*if the rectangle is under the page bottom but it can fit in a page,
  2007.         don't print it now*/
  2008.         if (html_redraw_printing) {
  2009.                 if (r.y1 > html_redraw_printing_border) {
  2010.                         if (r.y1 - r.y0 <= html_redraw_printing_border &&
  2011.                                         (box->type == BOX_TEXT ||
  2012.                                         box->type == BOX_TABLE_CELL
  2013.                                         || box->object || box->gadget)) {
  2014.                                 /*remember the highest of all points from the
  2015.                                 not printed elements*/
  2016.                                 if (r.y0 < html_redraw_printing_top_cropped)
  2017.                                         html_redraw_printing_top_cropped = r.y0;
  2018.                                 return true;
  2019.                         }
  2020.                 }
  2021.                 else box->flags |= PRINTED; /*it won't be printed anymore*/
  2022.         }
  2023.  
  2024.         /* if visibility is hidden render children only */
  2025.         if (box->style && css_computed_visibility(box->style) ==
  2026.                         CSS_VISIBILITY_HIDDEN) {
  2027.                 if ((plot->group_start) && (!plot->group_start("hidden box")))
  2028.                         return false;
  2029.                 if (!html_redraw_box_children(html, box, x_parent, y_parent,
  2030.                                 &r, scale, current_background_color, ctx))
  2031.                         return false;
  2032.                 return ((!plot->group_end) || (plot->group_end()));
  2033.         }
  2034.  
  2035.         if ((plot->group_start) && (!plot->group_start("vis box")))
  2036.                 return false;
  2037.  
  2038.  
  2039.         if (box->style != NULL &&
  2040.                         css_computed_position(box->style) ==
  2041.                                         CSS_POSITION_ABSOLUTE &&
  2042.                         css_computed_clip(box->style, &css_rect) ==
  2043.                                         CSS_CLIP_RECT) {
  2044.                 /* We have an absolutly positioned box with a clip rect */
  2045.                 if (css_rect.left_auto == false)
  2046.                         r.x0 = x - border_left + FIXTOINT(nscss_len2px(
  2047.                                         css_rect.left, css_rect.lunit,
  2048.                                         box->style));
  2049.  
  2050.                 if (css_rect.top_auto == false)
  2051.                         r.y0 = y - border_top + FIXTOINT(nscss_len2px(
  2052.                                         css_rect.top, css_rect.tunit,
  2053.                                         box->style));
  2054.  
  2055.                 if (css_rect.right_auto == false)
  2056.                         r.x1 = x - border_left + FIXTOINT(nscss_len2px(
  2057.                                         css_rect.right, css_rect.runit,
  2058.                                         box->style));
  2059.  
  2060.                 if (css_rect.bottom_auto == false)
  2061.                         r.y1 = y - border_top + FIXTOINT(nscss_len2px(
  2062.                                         css_rect.bottom, css_rect.bunit,
  2063.                                         box->style));
  2064.  
  2065.                 /* find intersection of clip rectangle and box */
  2066.                 if (r.x0 < clip->x0) r.x0 = clip->x0;
  2067.                 if (r.y0 < clip->y0) r.y0 = clip->y0;
  2068.                 if (clip->x1 < r.x1) r.x1 = clip->x1;
  2069.                 if (clip->y1 < r.y1) r.y1 = clip->y1;
  2070.                 /* no point trying to draw 0-width/height boxes */
  2071.                 if (r.x0 == r.x1 || r.y0 == r.y1)
  2072.                         /* not an error */
  2073.                         return ((!plot->group_end) || (plot->group_end()));
  2074.                 /* clip to it */
  2075.                 if (!plot->clip(&r))
  2076.                         return false;
  2077.  
  2078.         } else if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
  2079.                         box->type == BOX_TABLE_CELL || box->object) {
  2080.                 /* find intersection of clip rectangle and box */
  2081.                 if (r.x0 < clip->x0) r.x0 = clip->x0;
  2082.                 if (r.y0 < clip->y0) r.y0 = clip->y0;
  2083.                 if (clip->x1 < r.x1) r.x1 = clip->x1;
  2084.                 if (clip->y1 < r.y1) r.y1 = clip->y1;
  2085.                 /* no point trying to draw 0-width/height boxes */
  2086.                 if (r.x0 == r.x1 || r.y0 == r.y1)
  2087.                         /* not an error */
  2088.                         return ((!plot->group_end) || (plot->group_end()));
  2089.                 /* clip to it */
  2090.                 if (!plot->clip(&r))
  2091.                         return false;
  2092.         } else {
  2093.                 /* clip box is fine, clip to it */
  2094.                 r = *clip;
  2095.                 if (!plot->clip(&r))
  2096.                         return false;
  2097.         }
  2098.  
  2099.         /* background colour and image for block level content and replaced
  2100.          * inlines */
  2101.  
  2102.         bg_box = html_redraw_find_bg_box(box);
  2103.  
  2104.         /* bg_box == NULL implies that this box should not have
  2105.         * its background rendered. Otherwise filter out linebreaks,
  2106.         * optimize away non-differing inlines, only plot background
  2107.         * for BOX_TEXT it's in an inline */
  2108.         if (bg_box && bg_box->type != BOX_BR &&
  2109.                         bg_box->type != BOX_TEXT &&
  2110.                         bg_box->type != BOX_INLINE_END &&
  2111.                         (bg_box->type != BOX_INLINE || bg_box->object ||
  2112.                         bg_box->flags & IFRAME || box->flags & REPLACE_DIM)) {
  2113.                 /* find intersection of clip box and border edge */
  2114.                 struct rect p;
  2115.                 p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left;
  2116.                 p.y0 = y - border_top < r.y0 ? r.y0 : y - border_top;
  2117.                 p.x1 = x + padding_width + border_right < r.x1 ?
  2118.                                 x + padding_width + border_right : r.x1;
  2119.                 p.y1 = y + padding_height + border_bottom < r.y1 ?
  2120.                                 y + padding_height + border_bottom : r.y1;
  2121.                 if (!box->parent) {
  2122.                         /* Root element, special case:
  2123.                          * background covers margins too */
  2124.                         int m_left, m_top, m_right, m_bottom;
  2125.                         if (scale == 1.0) {
  2126.                                 m_left = box->margin[LEFT];
  2127.                                 m_top = box->margin[TOP];
  2128.                                 m_right = box->margin[RIGHT];
  2129.                                 m_bottom = box->margin[BOTTOM];
  2130.                         } else {
  2131.                                 m_left = box->margin[LEFT] * scale;
  2132.                                 m_top = box->margin[TOP] * scale;
  2133.                                 m_right = box->margin[RIGHT] * scale;
  2134.                                 m_bottom = box->margin[BOTTOM] * scale;
  2135.                         }
  2136.                         p.x0 = p.x0 - m_left < r.x0 ? r.x0 : p.x0 - m_left;
  2137.                         p.y0 = p.y0 - m_top < r.y0 ? r.y0 : p.y0 - m_top;
  2138.                         p.x1 = p.x1 + m_right < r.x1 ? p.x1 + m_right : r.x1;
  2139.                         p.y1 = p.y1 + m_bottom < r.y1 ? p.y1 + m_bottom : r.y1;
  2140.                 }
  2141.                 /* valid clipping rectangles only */
  2142.                 if ((p.x0 < p.x1) && (p.y0 < p.y1)) {
  2143.                         /* plot background */
  2144.                         if (!html_redraw_background(x, y, box, scale, &p,
  2145.                                         &current_background_color, bg_box, ctx))
  2146.                                 return false;
  2147.                         /* restore previous graphics window */
  2148.                         if (!plot->clip(&r))
  2149.                                 return false;
  2150.                 }
  2151.         }
  2152.  
  2153.         /* borders for block level content and replaced inlines */
  2154.         if (box->style && box->type != BOX_TEXT &&
  2155.                         box->type != BOX_INLINE_END &&
  2156.                         (box->type != BOX_INLINE || box->object ||
  2157.                         box->flags & IFRAME || box->flags & REPLACE_DIM) &&
  2158.                         (border_top || border_right ||
  2159.                          border_bottom || border_left)) {
  2160.                 if (!html_redraw_borders(box, x_parent, y_parent,
  2161.                                 padding_width, padding_height, &r,
  2162.                                 scale, ctx))
  2163.                         return false;
  2164.         }
  2165.  
  2166.         /* backgrounds and borders for non-replaced inlines */
  2167.         if (box->style && box->type == BOX_INLINE && box->inline_end &&
  2168.                         (html_redraw_box_has_background(box) ||
  2169.                         border_top || border_right ||
  2170.                         border_bottom || border_left)) {
  2171.                 /* inline backgrounds and borders span other boxes and may
  2172.                  * wrap onto separate lines */
  2173.                 struct box *ib;
  2174.                 struct rect b; /* border edge rectangle */
  2175.                 struct rect p; /* clipped rect */
  2176.                 bool first = true;
  2177.                 int ib_x;
  2178.                 int ib_y = y;
  2179.                 int ib_p_width;
  2180.                 int ib_b_left, ib_b_right;
  2181.  
  2182.                 b.x0 = x - border_left;
  2183.                 b.x1 = x + padding_width + border_right;
  2184.                 b.y0 = y - border_top;
  2185.                 b.y1 = y + padding_height + border_bottom;
  2186.  
  2187.                 p.x0 = b.x0 < r.x0 ? r.x0 : b.x0;
  2188.                 p.x1 = b.x1 < r.x1 ? b.x1 : r.x1;
  2189.                 p.y0 = b.y0 < r.y0 ? r.y0 : b.y0;
  2190.                 p.y1 = b.y1 < r.y1 ? b.y1 : r.y1;
  2191.                 for (ib = box; ib; ib = ib->next) {
  2192.                         /* to get extents of rectangle(s) associated with
  2193.                          * inline, cycle though all boxes in inline, skipping
  2194.                          * over floats */
  2195.                         if (ib->type == BOX_FLOAT_LEFT ||
  2196.                                         ib->type == BOX_FLOAT_RIGHT)
  2197.                                 continue;
  2198.                         if (scale == 1.0) {
  2199.                                 ib_x = x_parent + ib->x;
  2200.                                 ib_y = y_parent + ib->y;
  2201.                                 ib_p_width = ib->padding[LEFT] + ib->width +
  2202.                                                 ib->padding[RIGHT];
  2203.                                 ib_b_left = ib->border[LEFT].width;
  2204.                                 ib_b_right = ib->border[RIGHT].width;
  2205.                         } else {
  2206.                                 ib_x = (x_parent + ib->x) * scale;
  2207.                                 ib_y = (y_parent + ib->y) * scale;
  2208.                                 ib_p_width = (ib->padding[LEFT] + ib->width +
  2209.                                                 ib->padding[RIGHT]) * scale;
  2210.                                 ib_b_left = ib->border[LEFT].width * scale;
  2211.                                 ib_b_right = ib->border[RIGHT].width * scale;
  2212.                         }
  2213.  
  2214.                         if ((ib->flags & NEW_LINE) && ib != box) {
  2215.                                 /* inline element has wrapped, plot background
  2216.                                  * and borders */
  2217.                                 if (!html_redraw_inline_background(
  2218.                                                 x, y, box, scale, &p, b,
  2219.                                                 first, false,
  2220.                                                 &current_background_color, ctx))
  2221.                                         return false;
  2222.                                 /* restore previous graphics window */
  2223.                                 if (!plot->clip(&r))
  2224.                                         return false;
  2225.                                 if (!html_redraw_inline_borders(box, b, &r,
  2226.                                                 scale, first, false, ctx))
  2227.                                         return false;
  2228.                                 /* reset coords */
  2229.                                 b.x0 = ib_x - ib_b_left;
  2230.                                 b.y0 = ib_y - border_top - padding_top;
  2231.                                 b.y1 = ib_y + padding_height - padding_top +
  2232.                                                 border_bottom;
  2233.  
  2234.                                 p.x0 = b.x0 < r.x0 ? r.x0 : b.x0;
  2235.                                 p.y0 = b.y0 < r.y0 ? r.y0 : b.y0;
  2236.                                 p.y1 = b.y1 < r.y1 ? b.y1 : r.y1;
  2237.  
  2238.                                 first = false;
  2239.                         }
  2240.  
  2241.                         /* increase width for current box */
  2242.                         b.x1 = ib_x + ib_p_width + ib_b_right;
  2243.                         p.x1 = b.x1 < r.x1 ? b.x1 : r.x1;
  2244.  
  2245.                         if (ib == box->inline_end)
  2246.                                 /* reached end of BOX_INLINE span */
  2247.                                 break;
  2248.                 }
  2249.                 /* plot background and borders for last rectangle of
  2250.                  * the inline */
  2251.                 if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b,
  2252.                                 first, true, &current_background_color, ctx))
  2253.                         return false;
  2254.                 /* restore previous graphics window */
  2255.                 if (!plot->clip(&r))
  2256.                         return false;
  2257.                 if (!html_redraw_inline_borders(box, b, &r, scale, first, true,
  2258.                                 ctx))
  2259.                         return false;
  2260.  
  2261.         }
  2262.  
  2263.         /* Debug outlines */
  2264.         if (html_redraw_debug) {
  2265.                 int margin_left, margin_right;
  2266.                 int margin_top, margin_bottom;
  2267.                 if (scale == 1.0) {
  2268.                         /* avoid trivial fp maths */
  2269.                         margin_left = box->margin[LEFT];
  2270.                         margin_top = box->margin[TOP];
  2271.                         margin_right = box->margin[RIGHT];
  2272.                         margin_bottom = box->margin[BOTTOM];
  2273.                 } else {
  2274.                         margin_left = box->margin[LEFT] * scale;
  2275.                         margin_top = box->margin[TOP] * scale;
  2276.                         margin_right = box->margin[RIGHT] * scale;
  2277.                         margin_bottom = box->margin[BOTTOM] * scale;
  2278.                 }
  2279.                 /* Content edge -- blue */
  2280.                 if (!plot->rectangle(x + padding_left,
  2281.                                 y + padding_top,
  2282.                                 x + padding_left + width,
  2283.                                 y + padding_top + height,
  2284.                                 plot_style_content_edge))
  2285.                         return false;
  2286.                 /* Padding edge -- red */
  2287.                 if (!plot->rectangle(x, y,
  2288.                                 x + padding_width, y + padding_height,
  2289.                                 plot_style_padding_edge))
  2290.                         return false;
  2291.                 /* Margin edge -- yellow */
  2292.                 if (!plot->rectangle(
  2293.                                 x - border_left - margin_left,
  2294.                                 y - border_top - margin_top,
  2295.                                 x + padding_width + border_right +
  2296.                                                 margin_right,
  2297.                                 y + padding_height + border_bottom +
  2298.                                                 margin_bottom,
  2299.                                 plot_style_margin_edge))
  2300.                         return false;
  2301.         }
  2302.  
  2303.         /* clip to the padding edge for objects, or boxes with overflow hidden
  2304.          * or scroll */
  2305.         if ((box->style && css_computed_overflow(box->style) !=
  2306.                         CSS_OVERFLOW_VISIBLE) || box->object ||
  2307.                         box->flags & IFRAME) {
  2308.                 r.x0 = x;
  2309.                 r.y0 = y;
  2310.                 r.x1 = x + padding_width;
  2311.                 r.y1 = y + padding_height;
  2312.                 if (r.x0 < clip->x0) r.x0 = clip->x0;
  2313.                 if (r.y0 < clip->y0) r.y0 = clip->y0;
  2314.                 if (clip->x1 < r.x1) r.x1 = clip->x1;
  2315.                 if (clip->y1 < r.y1) r.y1 = clip->y1;
  2316.                 if (r.x1 <= r.x0 || r.y1 <= r.y0)
  2317.                         return ((!plot->group_end) || (plot->group_end()));
  2318.                 if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
  2319.                                 box->type == BOX_TABLE_CELL || box->object) {
  2320.                         if (!plot->clip(&r))
  2321.                                 return false;
  2322.                 }
  2323.         }
  2324.  
  2325.         /* text decoration */
  2326.         if (box->type != BOX_TEXT && box->style &&
  2327.                         css_computed_text_decoration(box->style) !=
  2328.                         CSS_TEXT_DECORATION_NONE)
  2329.                 if (!html_redraw_text_decoration(box, x_parent, y_parent,
  2330.                                 scale, current_background_color, ctx))
  2331.                         return false;
  2332.  
  2333.         if (box->object && width != 0 && height != 0) {
  2334.                 struct content_redraw_data obj_data;
  2335.  
  2336.                 x_scrolled = x - scrollbar_get_offset(box->scroll_x) * scale;
  2337.                 y_scrolled = y - scrollbar_get_offset(box->scroll_y) * scale;
  2338.  
  2339.                 obj_data.x = x_scrolled + padding_left;
  2340.                 obj_data.y = y_scrolled + padding_top;
  2341.                 obj_data.width = width;
  2342.                 obj_data.height = height;
  2343.                 obj_data.background_colour = current_background_color;
  2344.                 obj_data.scale = scale;
  2345.                 obj_data.repeat_x = false;
  2346.                 obj_data.repeat_y = false;
  2347.  
  2348.                 if (content_get_type(box->object) == CONTENT_HTML) {
  2349.                         obj_data.x /= scale;
  2350.                         obj_data.y /= scale;
  2351.                 }
  2352.  
  2353.                 if (!content_redraw(box->object, &obj_data, &r, ctx)) {
  2354.                         /* Show image fail */
  2355.                         /* Unicode (U+FFFC) 'OBJECT REPLACEMENT CHARACTER' */
  2356.                         const char *obj = "\xef\xbf\xbc";
  2357.                         int obj_width;
  2358.                         int obj_x = x + padding_left;
  2359.                         if (!plot->rectangle(x + padding_left,
  2360.                                         y + padding_top,
  2361.                                         x + padding_left + width - 1,
  2362.                                         y + padding_top + height - 1,
  2363.                                         plot_style_broken_object))
  2364.                                 return false;
  2365.                         if (!nsfont.font_width(plot_fstyle_broken_object, obj,
  2366.                                         sizeof(obj) - 1, &obj_width))
  2367.                                 obj_x += 1;
  2368.                         else
  2369.                                 obj_x += width / 2 - obj_width / 2;
  2370.  
  2371.                         if (!plot->text(obj_x, y + padding_top + (int)
  2372.                                                 (height * 0.75),
  2373.                                         obj, sizeof(obj) - 1,
  2374.                                         plot_fstyle_broken_object))
  2375.                                 return false;
  2376.                 }
  2377.                        
  2378.  
  2379.         } else if (box->iframe) {
  2380.                 /* Offset is passed to browser window redraw unscaled */
  2381.                 browser_window_redraw(box->iframe,
  2382.                                 (x + padding_left) / scale,
  2383.                                 (y + padding_top) / scale, &r, ctx);
  2384.  
  2385.         } else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) {
  2386.                 if (!html_redraw_checkbox(x + padding_left, y + padding_top,
  2387.                                 width, height, box->gadget->selected, ctx))
  2388.                         return false;
  2389.  
  2390.         } else if (box->gadget && box->gadget->type == GADGET_RADIO) {
  2391.                 if (!html_redraw_radio(x + padding_left, y + padding_top,
  2392.                                 width, height, box->gadget->selected, ctx))
  2393.                         return false;
  2394.  
  2395.         } else if (box->gadget && box->gadget->type == GADGET_FILE) {
  2396.                 if (!html_redraw_file(x + padding_left, y + padding_top,
  2397.                                 width, height, box, scale,
  2398.                                 current_background_color, ctx))
  2399.                         return false;
  2400.  
  2401.         } else if (box->text) {
  2402.                 if (!html_redraw_text_box(html, box, x, y, &r, scale,
  2403.                                 current_background_color, ctx))
  2404.                         return false;
  2405.  
  2406.         } else {
  2407.                 if (!html_redraw_box_children(html, box, x_parent, y_parent, &r,
  2408.                                 scale, current_background_color, ctx))
  2409.                         return false;
  2410.         }
  2411.  
  2412.         if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
  2413.                         box->type == BOX_TABLE_CELL || box->type == BOX_INLINE)
  2414.                 if (!plot->clip(clip))
  2415.                         return false;
  2416.  
  2417.         /* list marker */
  2418.         if (box->list_marker)
  2419.                 if (!html_redraw_box(html, box->list_marker,
  2420.                                 x_parent + box->x -
  2421.                                 scrollbar_get_offset(box->scroll_x),
  2422.                                 y_parent + box->y -
  2423.                                 scrollbar_get_offset(box->scroll_y),
  2424.                                 clip, scale, current_background_color, ctx))
  2425.                         return false;
  2426.  
  2427.         /* scrollbars */
  2428.         if (((box->style && box->type != BOX_BR &&
  2429.                         box->type != BOX_TABLE && box->type != BOX_INLINE &&
  2430.                         (css_computed_overflow(box->style) ==
  2431.                                 CSS_OVERFLOW_SCROLL ||
  2432.                         css_computed_overflow(box->style) ==
  2433.                                 CSS_OVERFLOW_AUTO)) || (box->object &&
  2434.                         content_get_type(box->object) == CONTENT_HTML)) &&
  2435.                         box->parent != NULL) {
  2436.  
  2437.                 has_x_scroll = box_hscrollbar_present(box);
  2438.                 has_y_scroll = box_vscrollbar_present(box);
  2439.  
  2440.                 if (!box_handle_scrollbars((struct content *)html,
  2441.                                 box, has_x_scroll, has_y_scroll))
  2442.                         return false;
  2443.                
  2444.                 if (box->scroll_x != NULL)
  2445.                         scrollbar_redraw(box->scroll_x,
  2446.                                         x_parent + box->x,
  2447.                                         y_parent + box->y + box->padding[TOP] +
  2448.                                         box->height + box->padding[BOTTOM] -
  2449.                                         SCROLLBAR_WIDTH, clip, scale, ctx);
  2450.                 if (box->scroll_y != NULL)
  2451.                         scrollbar_redraw(box->scroll_y,
  2452.                                         x_parent + box->x + box->padding[LEFT] +
  2453.                                         box->width + box->padding[RIGHT] -
  2454.                                         SCROLLBAR_WIDTH,
  2455.                                         y_parent + box->y, clip, scale, ctx);
  2456.         }
  2457.  
  2458.         if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
  2459.                         box->type == BOX_TABLE_CELL || box->type == BOX_INLINE)
  2460.                 if (!plot->clip(clip))
  2461.                         return false;
  2462.  
  2463.         return ((!plot->group_end) || (plot->group_end()));
  2464. }
  2465.  
  2466. /**
  2467.  * Draw a CONTENT_HTML using the current set of plotters (plot).
  2468.  *
  2469.  * \param  c     content of type CONTENT_HTML
  2470.  * \param  data  redraw data for this content redraw
  2471.  * \param  clip  current clip region
  2472.  * \param  ctx   current redraw context
  2473.  * \return true if successful, false otherwise
  2474.  *
  2475.  * x, y, clip_[xy][01] are in target coordinates.
  2476.  */
  2477.  
  2478. bool html_redraw(struct content *c, struct content_redraw_data *data,
  2479.                 const struct rect *clip, const struct redraw_context *ctx)
  2480. {
  2481.         html_content *html = (html_content *) c;
  2482.         struct box *box;
  2483.         bool result = true;
  2484.         bool select, select_only;
  2485.         plot_style_t pstyle_fill_bg = {
  2486.                 .fill_type = PLOT_OP_TYPE_SOLID,
  2487.                 .fill_colour = data->background_colour,
  2488.         };
  2489.  
  2490.         box = html->layout;
  2491.         assert(box);
  2492.  
  2493.         /* The select menu needs special treating because, when opened, it
  2494.          * reaches beyond its layout box.
  2495.          */
  2496.         select = false;
  2497.         select_only = false;
  2498.         if (ctx->interactive && html->visible_select_menu != NULL) {
  2499.                 struct form_control *control = html->visible_select_menu;
  2500.                 select = true;
  2501.                 /* check if the redraw rectangle is completely inside of the
  2502.                    select menu */
  2503.                 select_only = form_clip_inside_select_menu(control,
  2504.                                 data->scale, clip);
  2505.         }
  2506.        
  2507.         if (!select_only) {
  2508.                 /* clear to background colour */
  2509.                 result = ctx->plot->clip(clip);
  2510.        
  2511.                 if (html->background_colour != NS_TRANSPARENT)
  2512.                         pstyle_fill_bg.fill_colour = html->background_colour;
  2513.        
  2514.                 result &= ctx->plot->rectangle(clip->x0, clip->y0,
  2515.                                 clip->x1, clip->y1,
  2516.                                 &pstyle_fill_bg);
  2517.        
  2518.                 result &= html_redraw_box(html, box, data->x, data->y, clip,
  2519.                                 data->scale, pstyle_fill_bg.fill_colour, ctx);
  2520.         }
  2521.  
  2522.         if (select) {
  2523.                 int menu_x, menu_y;
  2524.                 box = html->visible_select_menu->box;
  2525.                 box_coords(box, &menu_x, &menu_y);
  2526.        
  2527.                 menu_x -= box->border[LEFT].width;
  2528.                 menu_y += box->height + box->border[BOTTOM].width +
  2529.                                 box->padding[BOTTOM] + box->padding[TOP];
  2530.                 result &= form_redraw_select_menu(html->visible_select_menu,
  2531.                                 data->x + menu_x, data->y + menu_y,
  2532.                                 data->scale, clip, ctx);
  2533.         }
  2534.  
  2535.         return result;
  2536.  
  2537. }
  2538.