All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.openhtmltopdf.render.BorderPainter Maven / Gradle / Ivy

Go to download

Open HTML to PDF is a CSS 2.1 renderer written in Java. This artifact contains the core rendering and layout code.

There is a newer version: 1.1.4
Show newest version
/*
 * {{{ header & license
 * Copyright (c) 2004, 2005 Joshua Marinacci
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * }}}
 */
package com.openhtmltopdf.render;

import java.awt.BasicStroke;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;

import com.openhtmltopdf.css.constants.IdentValue;
import com.openhtmltopdf.css.parser.FSColor;
import com.openhtmltopdf.css.parser.FSRGBColor;
import com.openhtmltopdf.css.style.BorderRadiusCorner;
import com.openhtmltopdf.css.style.derived.BorderPropertySet;
import com.openhtmltopdf.extend.OutputDevice;


public class BorderPainter {
    public static final int TOP = 1;
    public static final int LEFT = 2;
    public static final int BOTTOM = 4;
    public static final int RIGHT = 8;
    public static final int ALL = TOP + LEFT + BOTTOM + RIGHT;

    /**
     * Generate a simple rectangle without beveling for a solid border side.
     * Turning off beveling should disable anti-aliasing and work better with
     * table cell borders.
     * See https://github.com/danfickle/openhtmltopdf/issues/752
     */
    private static Shape generateSimpleBorderShape(Rectangle bounds, int currentSide, BorderPropertySet border) {
        if (currentSide == TOP || currentSide == BOTTOM) {
            double x = bounds.getX();
            double y = currentSide == TOP ?
                    bounds.getY() :
                    bounds.getY() + bounds.getHeight() - border.bottom();
            double w = bounds.getWidth();
            double h = currentSide == TOP ?
                    border.top() : border.bottom();

            return new Rectangle2D.Double(x, y, w, h);
        } else {
            double x = currentSide == LEFT ?
                    bounds.getX() :
                    bounds.getX() + bounds.getWidth() - border.right();
            double y = bounds.getY();
            double w = currentSide == LEFT ?
                    border.left() : border.right();
            double h = bounds.getHeight();

            return new Rectangle2D.Double(x, y, w, h);
        }
    }

    /**
     * Generates a full round rectangle that is made of bounds and border
     * @param bounds Dimmensions of the rect
     * @param border The border specs
     * @param inside true if you want the inner bounds of borders
     * @return A Path that is all sides of the round rectangle
     */
    public static Path2D generateBorderBounds(Rectangle bounds, BorderPropertySet border, boolean inside) {
        Path2D path = generateBorderShape(bounds, TOP, border, false, inside ? 1 : 0, 1);
        path.append(generateBorderShape(bounds, RIGHT, border, false, inside ? 1 : 0, 1), true);
        path.append(generateBorderShape(bounds, BOTTOM, border, false, inside ? 1 : 0, 1), true);
        path.append(generateBorderShape(bounds, LEFT, border, false, inside ? 1 : 0, 1), true);
        return path;
    }
    

    /**
     * Generates one side of a border
     * @param bounds bounds of the container
     * @param side what side you want
     * @param border border props
     * @param drawInterior if you want it to be 2d or not, if false it will be just a line
     * @return a path for the side chosen!
     */
    public static Path2D generateBorderShape(Rectangle bounds, int side, BorderPropertySet border, boolean drawInterior) {
        return generateBorderShape(bounds, side, border, drawInterior, 0, 1);
    }
    /**
     * Generates one side of a border
     * @param bounds bounds of the container
     * @param side what side you want
     * @param border border props
     * @param drawInterior if you want it to be 2d or not, if false it will be just a line
     * @param scaledOffset insets the border by multipling border widths by this variable, best use would be 1 or .5, cant see it for much other than that
     * @return a path for the side chosen!
     */
    public static Path2D generateBorderShape(Rectangle bounds, int side, BorderPropertySet border, boolean drawInterior, float scaledOffset) {
        return generateBorderShape(bounds, side, border, drawInterior, scaledOffset, 1);
    }
    
    /**
     * Generates one side of a border
     * @param bounds bounds of the container
     * @param side what side you want
     * @param border border props
     * @param drawInterior if you want it to be 2d or not, if false it will be just a line
     * @param scaledOffset insets the border by multipling border widths by this variable, best use would be 1 or .5, cant see it for much other than that
     * @param widthScale scales the border widths by this factor, useful for drawing half borders for border types like groove or double
     * @return a path for the side chosen!
     */
    public static Path2D generateBorderShape(Rectangle bounds, int side, BorderPropertySet border, boolean drawInterior, float scaledOffset, float widthScale) {
        /**
         * Function overview: Prior to creating the path we check what side were building this on. All the coordinates in this function assume its building a top border
         * the border is then rotated and translated to its appropriate side. Uses of "left" and "right" are assuming a perspective of inside the shape looking out.
         */
        border = border.normalizedInstance(new Rectangle(bounds.width, bounds.height));
        
        RelativeBorderProperties props = new RelativeBorderProperties(bounds, border, 0f, side, 1+scaledOffset, widthScale);
        float sideWidth;
        if(props.isDimmensionsSwapped()) {
            sideWidth = bounds.height-(1+scaledOffset)*(widthScale)*(border.top()+border.bottom());
        } else {
            sideWidth = bounds.width-(1+scaledOffset)*(widthScale)*(border.left()+border.right());
        }
        Path2D path = new Path2D.Float();
        
        float angle = 90;
        float widthSum = props.getTop() + props.getLeft();
        
        if (widthSum != 0f) {
            angle = angle * props.getTop() / widthSum;
        }
        
        appendPath(path, 0-props.getLeft(), 0-props.getTop(), props.getLeftCorner().left(), props.getLeftCorner().right(), 90+angle, -angle-1, props.getTop(), props.getLeft(), scaledOffset, true, widthScale);
        
        angle = 90;
        widthSum = props.getTop() + props.getRight();
        
        if (widthSum != 0f) {
        	angle = angle * props.getTop() / widthSum;
        }
        
        appendPath(path, sideWidth+props.getRight(), 0-props.getTop(), props.getRightCorner().right(), props.getRightCorner().left(), 90, -angle-1, props.getTop(), props.getRight(), scaledOffset, false, widthScale);
        
        
        if(drawInterior) {
            //border = border.normalizeBorderRadius(new Rectangle((int)(bounds.width), (int)(bounds.height)));
            //props = new RelativeBorderProperties(bounds, border, 0f, side, 1+scaledOffset, 1);
            
            appendPath(path, sideWidth, 0, props.getRightCorner().right(), props.getRightCorner().left(), 90-angle, angle+1, props.getTop(), props.getRight(), scaledOffset+1, false, widthScale);
            
            angle = 90;
            widthSum = props.getTop() + props.getLeft();
            
            if (widthSum != 0f) {
            	angle = angle * props.getTop() / widthSum;
            }
            
            appendPath(path, 0, 0, props.getLeftCorner().left(), props.getLeftCorner().right(), 90, angle+1, props.getTop(), props.getLeft(), scaledOffset+1, true, widthScale);
            
            path.closePath();
        }
        

        path.transform(AffineTransform.getTranslateInstance( 
                (!props.isDimmensionsSwapped() ? -bounds.width/2f : -bounds.height/2f) + (scaledOffset+1)*props.getLeft(),
                (props.isDimmensionsSwapped() ? -bounds.width/2f : -bounds.height/2f) + (scaledOffset+1)*props.getTop()));
        path.transform(AffineTransform.getRotateInstance(
                props.getRotation()));
        path.transform(AffineTransform.getTranslateInstance( 
                bounds.width/2f+bounds.x, bounds.height/2f+bounds.y));
        
        return path;
    }
    
    private static void appendPath(Path2D path, float xOffset, float yOffset, float radiusVert, float radiusHoriz, float startAngle, float distance, float topWidth, float sideWidth, float scaleOffset, boolean left, float widthScale) {
        float innerWidth = 2*radiusHoriz - scaleOffset*sideWidth - scaleOffset*sideWidth;
        float innerHeight = 2*radiusVert - scaleOffset*topWidth - scaleOffset*topWidth;
        
        if(innerWidth > 0 && innerHeight > 0) {
            // do arc
            Arc2D arc = new Arc2D.Float(
                    xOffset-(left?0:(innerWidth)), 
                    yOffset, 
                    innerWidth, 
                    innerHeight, startAngle, distance, Arc2D.OPEN);
            path.append(arc, true);
        } else {
            // do line
            if(path.getCurrentPoint() == null) {
                path.moveTo(xOffset, yOffset);
            } else {
                path.lineTo(xOffset, yOffset);
            }
        }
    }

    private static class RelativeBorderProperties {
        private final float _top;
        private final float _left;
        private final float _right;
        private final BorderRadiusCorner _leftCorner;
        private final BorderRadiusCorner _rightCorner;
        
        private final double _rotation;
        private final boolean _dimmensionsSwapped;
        
        public RelativeBorderProperties(Rectangle bounds, BorderPropertySet props, float borderScaleOffset,  int side, float scaledOffset, float widthScale) {

            if ((side & BorderPainter.TOP) == BorderPainter.TOP) {
                _top = props.top()*widthScale;
                _left = props.left()*widthScale;
                _right = props.right()*widthScale;
                _leftCorner = props.getTopLeft();
                _rightCorner = props.getTopRight();
                _rotation = 0;
                _dimmensionsSwapped = false;
            } else if ((side & BorderPainter.RIGHT) == BorderPainter.RIGHT) {
                _top = props.right()*widthScale;
                _left = props.top()*widthScale;
                _right = props.bottom()*widthScale;
                _leftCorner = props.getTopRight();
                _rightCorner = props.getBottomRight();
                _rotation = Math.PI/2;
                _dimmensionsSwapped = true;
            } else if ((side & BorderPainter.BOTTOM) == BorderPainter.BOTTOM) {
                _top = props.bottom()*widthScale;
                _left = props.right()*widthScale;
                _right = props.left()*widthScale;
                _leftCorner = props.getBottomRight();
                _rightCorner = props.getBottomLeft();
                _rotation = Math.PI;
                _dimmensionsSwapped = false;
            } else if ((side & BorderPainter.LEFT) == BorderPainter.LEFT) {
                _top = props.left()*widthScale;
                _left = props.bottom()*widthScale;
                _right = props.top()*widthScale;
                _leftCorner = props.getBottomLeft();
                _rightCorner = props.getTopLeft();
                _rotation = 3*Math.PI/2;
                _dimmensionsSwapped = true;
            } else {
                throw new IllegalArgumentException("No side found");
            }
        }
        
        public BorderRadiusCorner getRightCorner() {
            return _rightCorner;
        }
        public BorderRadiusCorner getLeftCorner() {
            return _leftCorner;
        }
        public float getTop() {
            return _top;
        }
        public float getLeft() {
            return _left;
        }
        public float getRight() {
            return _right;
        }

        private double getRotation() {
            return _rotation;
        }

        private boolean isDimmensionsSwapped() {
            return _dimmensionsSwapped;
        }
    }
    
    /**
     * @param xOffset for determining starting point for patterns
     */
    public static void paint(
            Rectangle bounds, int sides, BorderPropertySet border, 
            RenderingContext ctx, int xOffset, boolean bevel) {
        if ((sides & BorderPainter.TOP) == BorderPainter.TOP && border.noTop()) {
            sides -= BorderPainter.TOP;
        }
        if ((sides & BorderPainter.LEFT) == BorderPainter.LEFT && border.noLeft()) {
            sides -= BorderPainter.LEFT;
        }
        if ((sides & BorderPainter.BOTTOM) == BorderPainter.BOTTOM && border.noBottom()) {
            sides -= BorderPainter.BOTTOM;
        }
        if ((sides & BorderPainter.RIGHT) == BorderPainter.RIGHT && border.noRight()) {
            sides -= BorderPainter.RIGHT;
        }

        //Now paint!
        if ((sides & BorderPainter.TOP) == BorderPainter.TOP && border.topColor() != FSRGBColor.TRANSPARENT) {
            paintBorderSide(ctx.getOutputDevice(), 
                    border, bounds, sides, BorderPainter.TOP, border.topStyle(), xOffset, bevel);
        }
        if ((sides & BorderPainter.BOTTOM) == BorderPainter.BOTTOM && border.bottomColor() != FSRGBColor.TRANSPARENT) {
            paintBorderSide(ctx.getOutputDevice(), 
                    border, bounds, sides, BorderPainter.BOTTOM, border.bottomStyle(), xOffset, bevel);
        }
        if ((sides & BorderPainter.LEFT) == BorderPainter.LEFT && border.leftColor() != FSRGBColor.TRANSPARENT) {
            paintBorderSide(ctx.getOutputDevice(), 
                    border, bounds, sides, BorderPainter.LEFT, border.leftStyle(), xOffset, bevel);
        }
        if ((sides & BorderPainter.RIGHT) == BorderPainter.RIGHT && border.rightColor() != FSRGBColor.TRANSPARENT) {
            paintBorderSide(ctx.getOutputDevice(), 
                    border, bounds, sides, BorderPainter.RIGHT, border.rightStyle(), xOffset, bevel);
        }
    }

    private static void paintBorderSide(OutputDevice outputDevice, 
            final BorderPropertySet border, final Rectangle bounds, final int sides, 
            int currentSide, final IdentValue borderSideStyle, int xOffset, boolean bevel) {

        if (borderSideStyle == IdentValue.RIDGE || borderSideStyle == IdentValue.GROOVE) {
            BorderPropertySet bd2 = new BorderPropertySet(bevel, (int) (border.top() / 2),
                    (int) (border.right() / 2),
                    (int) (border.bottom() / 2),
                    (int) (border.left() / 2));
           BorderPropertySet borderA = null,
                   borderB = null;
           if (borderSideStyle == IdentValue.RIDGE) {
                borderA = border;
                borderB =  border.darken(borderSideStyle);
            } else {
                borderA =  border.darken(borderSideStyle);
                borderB = border;
            }
           paintBorderSideShape(
                   outputDevice, bounds, bd2, borderA,
                   borderB,
                   0, 1, sides, currentSide, bevel);
           paintBorderSideShape(
                   outputDevice, bounds, border, borderB,
                   borderA,
                   1, .5f, sides, currentSide, bevel);
        } else if (borderSideStyle == IdentValue.OUTSET) {
            paintBorderSideShape(outputDevice, bounds, border,
                    border,
                    border.darken(borderSideStyle), 
                    0, 1, sides, currentSide, bevel);
        } else if (borderSideStyle == IdentValue.INSET) {
            paintBorderSideShape(outputDevice, bounds, border,
                    border.darken(borderSideStyle),
                    border,
                    0, 1, sides, currentSide, bevel);
        } else if (borderSideStyle == IdentValue.SOLID) {
            FSColor bColor = null;

            switch (currentSide) {
            case TOP:
                bColor = border.topColor();
                break;
            case RIGHT:
                bColor = border.rightColor();
                break;
            case BOTTOM:
                bColor = border.bottomColor();
                break;
            case LEFT:
                bColor = border.leftColor();
                break;
            default:
                return;
            }

            Shape s = border.isBevelAllowed() || border.hasBorderRadius() ?
                    generateBorderShape(bounds, currentSide, border, true, 0, 1) :
                    generateSimpleBorderShape(bounds, currentSide, border);

            outputDevice.setStroke(new BasicStroke(1f));
            outputDevice.setColor(bColor);
            outputDevice.fill(s);

        } else if (borderSideStyle == IdentValue.DOUBLE) {
            paintDoubleBorder(outputDevice, border, bounds, sides, currentSide, bevel);
        } else {
            int thickness = 0;
            if (currentSide == BorderPainter.TOP) thickness = (int) border.top();
            if (currentSide == BorderPainter.BOTTOM) thickness = (int) border.bottom();
            if (currentSide == BorderPainter.RIGHT) thickness = (int) border.right();
            if (currentSide == BorderPainter.LEFT) thickness = (int) border.left();
            if (borderSideStyle == IdentValue.DASHED) {
                //outputDevice.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
                paintPatternedRect(outputDevice, bounds, border, border, new float[]{8.0f + thickness * 2, 4.0f + thickness}, sides, currentSide, xOffset);
                //outputDevice.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            }
            if (borderSideStyle == IdentValue.DOTTED) {
                // turn off anti-aliasing or the dots will be all blurry
                //outputDevice.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
                paintPatternedRect(outputDevice, bounds, border, border, new float[]{thickness, thickness}, sides, currentSide, xOffset);
                //outputDevice.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            }
        }
    }

    private static void paintDoubleBorder(
            OutputDevice outputDevice, BorderPropertySet border, 
            Rectangle bounds, int sides, int currentSide, boolean bevel) {
        // draw outer border
        paintSolid(outputDevice, bounds, border, 0, 1/3f, sides, currentSide, bevel);
        // draw inner border
        //paintSolid(outputDevice, bounds, border, 1, 1/3f, sides, currentSide, bevel);
        paintSolid(outputDevice, bounds, border, 2, 1/3f, sides, currentSide, bevel);
    }

    /**
     * @param xOffset     for inline borders, to determine dash_phase of top and bottom
     */
    private static void paintPatternedRect(OutputDevice outputDevice, 
            final Rectangle bounds, final BorderPropertySet border, 
            final BorderPropertySet color, final float[] pattern, 
            final int sides, final int currentSide, int xOffset) {
        Stroke old_stroke = outputDevice.getStroke();

        Path2D path = generateBorderShape(bounds, currentSide, border, false, .5f, 1);
        Area clip = new Area(generateBorderShape(bounds, currentSide, border, true, 0, 1));

        outputDevice.pushClip(clip);
        
        if (currentSide == BorderPainter.TOP) {
            outputDevice.setColor(color.topColor());
            outputDevice.setStroke(new BasicStroke(2*(int) border.top(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, pattern, xOffset));
            outputDevice.drawBorderLine(
                    path, BorderPainter.TOP, (int)border.top(), false);
        } else if (currentSide == BorderPainter.LEFT) {
            outputDevice.setColor(color.leftColor());
            outputDevice.setStroke(new BasicStroke(2*(int) border.left(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, pattern, 0));
            outputDevice.drawBorderLine(
                    path, BorderPainter.LEFT, (int)border.left(), false);
        } else if (currentSide == BorderPainter.RIGHT) {
            outputDevice.setColor(color.rightColor());
            outputDevice.setStroke(new BasicStroke(2*(int) border.right(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, pattern, 0));
            outputDevice.drawBorderLine(
                    path, BorderPainter.RIGHT, (int)border.right(), false);
        } else if (currentSide == BorderPainter.BOTTOM) {
            outputDevice.setColor(color.bottomColor());
            outputDevice.setStroke(new BasicStroke(2*(int) border.bottom(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, pattern, xOffset));
            outputDevice.drawBorderLine(
                    path, BorderPainter.BOTTOM, (int)border.bottom(), false);
        }

        outputDevice.popClip();
        
        outputDevice.setStroke(old_stroke);
    }

    private static void paintBorderSideShape(OutputDevice outputDevice, 
            final Rectangle bounds, final BorderPropertySet border, 
            final BorderPropertySet high, final BorderPropertySet low, 
            final float offset, final float scale,
            final int sides, int currentSide, boolean bevel) {
        if (currentSide == BorderPainter.TOP) {
            paintSolid(outputDevice, bounds, high, offset, scale, sides, currentSide, bevel);
        } else if (currentSide == BorderPainter.BOTTOM) {
            paintSolid(outputDevice, bounds, low, offset, scale, sides, currentSide, bevel);
        } else if (currentSide == BorderPainter.RIGHT) {
            paintSolid(outputDevice, bounds, low, offset, scale, sides, currentSide, bevel);
        } else if (currentSide == BorderPainter.LEFT) {
            paintSolid(outputDevice, bounds, high, offset, scale, sides, currentSide, bevel);
        }
    }

    private static void paintSolid(OutputDevice outputDevice, 
            final Rectangle bounds, final BorderPropertySet border, 
            final float offset, final float scale, final int sides, int currentSide,
            boolean bevel) {
        
        if (currentSide == BorderPainter.TOP) {
            outputDevice.setColor(border.topColor());
            // draw a 1px border with a line instead of a polygon
            if ((int) border.top() == 1) {
                Shape line = generateBorderShape(bounds, currentSide, border, false, offset, scale);
                outputDevice.draw(line);
            } else {
                Shape line = generateBorderShape(bounds, currentSide, border, true, offset, scale);
                // use polygons for borders over 1px wide
                outputDevice.fill(line);
            }
        } else if (currentSide == BorderPainter.BOTTOM) {
            outputDevice.setColor(border.bottomColor());
            if ((int) border.bottom() == 1) {
                Shape line = generateBorderShape(bounds, currentSide, border, false, offset, scale);
                outputDevice.draw(line);
            } else {
                Shape line = generateBorderShape(bounds, currentSide, border, true, offset, scale);
                // use polygons for borders over 1px wide
                outputDevice.fill(line);
            }
        } else if (currentSide == BorderPainter.RIGHT) {
            outputDevice.setColor(border.rightColor());
            if ((int) border.right() == 1) {
                Shape line = generateBorderShape(bounds, currentSide, border, false, offset, scale);
                outputDevice.draw(line);
            } else {
                Shape line = generateBorderShape(bounds, currentSide, border, true, offset, scale);
                // use polygons for borders over 1px wide
                outputDevice.fill(line);
            }
        } else if (currentSide == BorderPainter.LEFT) {
            outputDevice.setColor(border.leftColor());
            if ((int) border.left() == 1) {
                Shape line = generateBorderShape(bounds, currentSide, border, false, offset, scale);
                outputDevice.draw(line);
            } else {
                Shape line = generateBorderShape(bounds, currentSide, border, true, offset, scale);
                // use polygons for borders over 1px wide
                outputDevice.fill(line);
            }
        }
    }
}

/*
 * $Id$
 *
 * $Log$
 * Revision 1.48  2008/07/27 00:21:47  peterbrant
 * Implement CMYK color support for PDF output, starting with patch from Mykola Gurov / Banish java.awt.Color from FS core layout classes
 *
 * Revision 1.47  2007/04/24 17:04:31  peterbrant
 * Method name improvements
 *
 * Revision 1.46  2007/03/01 18:00:10  peterbrant
 * Fix rounding problems with double borders / Light BorderPainter cleanup (more needed) / Don't bevel collapsed table borders
 *
 * Revision 1.45  2007/02/07 16:33:25  peterbrant
 * Initial commit of rewritten table support and associated refactorings
 *
 * Revision 1.44  2007/01/17 17:50:54  peterbrant
 * Clean out unused code
 *
 * Revision 1.43  2006/02/21 18:09:17  peterbrant
 * Fix call to BorderPropertySet constructor
 *
 * Revision 1.42  2006/02/01 01:30:14  peterbrant
 * Initial commit of PDF work
 *
 * Revision 1.41  2006/01/27 01:15:36  peterbrant
 * Start on better support for different output devices
 *
 * Revision 1.40  2005/12/15 20:04:13  peterbrant
 * Don't paint transparent borders
 *
 * Revision 1.39  2005/11/08 20:02:14  peterbrant
 * Fix off by one errors for borders with an odd width
 *
 * Revision 1.38  2005/10/27 00:09:02  tobega
 * Sorted out Context into RenderingContext and LayoutContext
 *
 * Revision 1.37  2005/10/21 18:49:44  pdoubleya
 * Fixed border painting bug.
 *
 * Revision 1.36  2005/10/21 18:10:52  pdoubleya
 * Support for cachable borders. Still buggy on some pages, but getting there.
 *
 * Revision 1.35  2005/06/22 23:48:45  tobega
 * Refactored the css package to allow a clean separation from the core.
 *
 * Revision 1.34  2005/06/04 16:04:12  tobega
 * just playing with border colors a bit more
 *
 * Revision 1.33  2005/06/04 14:47:43  tobega
 * Just for fun: took more control over darkening/brightening colors. Looks nice, though.
 *
 * Revision 1.32  2005/06/03 01:08:58  tobega
 * Fixed bug in painting double borders
 *
 * Revision 1.31  2005/05/29 23:49:15  tobega
 * Did it right, this time, so that inline borders also look nice
 *
 * Revision 1.30  2005/05/29 23:43:28  tobega
 * Removed tendency for white diagonal line in corners
 *
 * Revision 1.29  2005/05/29 20:13:20  tobega
 * Cleaned up duplicate code
 *
 * Revision 1.28  2005/05/29 19:37:58  tobega
 * Fixed up using different style borders.
 * Fixed patterned borders to work right.
 *
 * Revision 1.27  2005/05/16 08:07:09  tobega
 * Border painting for inlines works beautifully (tested only solid borders)
 *
 * Revision 1.26  2005/05/13 11:49:59  tobega
 * Started to fix up borders on inlines. Got caught up in refactoring.
 * Boxes shouldn't cache borders and stuff unless necessary. Started to remove unnecessary references.
 * Hover is not working completely well now, might get better when I'm done.
 *
 * Revision 1.25  2005/05/13 08:46:17  tobega
 * A line is drawn to the right and below the coordinate. Needed to adjust when drawing lines for 1-pixel borders
 *
 * Revision 1.24  2005/05/12 06:24:16  joshy
 * more very minor border and background tweaks
 * Issue number:
 * Obtained from:
 * Submitted by:
 * Reviewed by:
 *
 * Revision 1.23  2005/05/12 04:55:57  joshy
 * fix for issues 76
 * Issue number:
 * Obtained from:
 * Submitted by:
 * Reviewed by:
 *
 * Revision 1.22  2005/05/08 14:36:58  tobega
 * Refactored away the need for having a context in a CalculatedStyle
 *
 * Revision 1.21  2005/01/29 20:24:23  pdoubleya
 * Clean/reformat code. Removed commented blocks, checked copyright.
 *
 * Revision 1.20  2005/01/25 10:56:56  pdoubleya
 * Added warning on possible duplicate code.
 *
 * Revision 1.19  2005/01/25 10:55:15  pdoubleya
 * Added warning on possible duplicate code.
 *
 * Revision 1.18  2005/01/24 22:46:42  pdoubleya
 * Added support for ident-checks using IdentValue instead of string comparisons.
 *
 * Revision 1.17  2005/01/24 14:36:34  pdoubleya
 * Mass commit, includes: updated for changes to property declaration instantiation, and new use of DerivedValue. Removed any references to older XR... classes (e.g. XRProperty). Cleaned imports.
 *
 * Revision 1.16  2005/01/09 15:22:49  tobega
 * Prepared improved handling of margins, borders and padding.
 *
 * Revision 1.15  2004/12/29 10:39:34  tobega
 * Separated current state Context into LayoutContext and the rest into SharedContext.
 *
 * Revision 1.14  2004/12/27 09:40:48  tobega
 * Moved more styling to render stage. Now inlines have backgrounds and borders again.
 *
 * Revision 1.13  2004/12/27 07:43:32  tobega
 * Cleaned out border from box, it can be gotten from current style. Is it maybe needed for dynamic stuff?
 *
 * Revision 1.12  2004/12/13 02:12:53  tobega
 * Borders are working again
 *
 * Revision 1.11  2004/12/12 04:18:57  tobega
 * Now the core compiles at least. Now we must make it work right. Table layout is one point that really needs to be looked over
 *
 * Revision 1.10  2004/12/12 03:33:00  tobega
 * Renamed x and u to avoid confusing IDE. But that got cvs in a twist. See if this does it
 *
 * Revision 1.9  2004/12/11 23:36:49  tobega
 * Progressing on cleaning up layout and boxes. Still broken, won't even compile at the moment. Working hard to fix it, though.
 *
 * Revision 1.8  2004/12/05 00:48:58  tobega
 * Cleaned up so that now all property-lookups use the CalculatedStyle. Also added support for relative values of top, left, width, etc.
 *
 * Revision 1.7  2004/11/09 15:53:49  joshy
 * initial support for hover (currently disabled)
 * moved justification code into it's own class in a new subpackage for inline
 * layout (because it's so blooming complicated)
 *
 * Issue number:
 * Obtained from:
 * Submitted by:
 * Reviewed by:
 *
 * Revision 1.6  2004/11/07 16:23:18  joshy
 * added support for lighten and darken to bordercolor
 * added support for different colored sides
 *
 * Issue number:
 * Obtained from:
 * Submitted by:
 * Reviewed by:
 *
 * Revision 1.5  2004/11/06 22:49:52  joshy
 * cleaned up alice
 * initial support for inline borders and backgrounds
 * moved all of inlinepainter back into inlinerenderer, where it belongs.
 *
 *
 *
 * Issue number:
 * Obtained from:
 * Submitted by:
 * Reviewed by:
 *
 * Revision 1.4  2004/11/02 17:14:00  joshy
 * implemented double borders
 *
 *
 * Issue number:
 * Obtained from:
 * Submitted by:
 * Reviewed by:
 *
 * Revision 1.3  2004/10/23 13:50:26  pdoubleya
 * Re-formatted using JavaStyle tool.
 * Cleaned imports to resolve wildcards except for common packages (java.io, java.util, etc).
 * Added CVS log comments at bottom.
 *
 *
 */





© 2015 - 2024 Weber Informatics LLC | Privacy Policy