/* * {{{ header & license * Copyright (c) 2004, 2005 Joshua Marinacci * Copyright (c) 2005, 2006 Wisconsin Court System * * 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 org.xhtmlrenderer.render; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.Shape; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xhtmlrenderer.css.constants.CSSName; import org.xhtmlrenderer.css.constants.IdentValue; import org.xhtmlrenderer.css.parser.FSColor; import org.xhtmlrenderer.css.parser.FSRGBColor; import org.xhtmlrenderer.css.style.CalculatedStyle; import org.xhtmlrenderer.css.style.CssContext; import org.xhtmlrenderer.css.style.derived.BorderPropertySet; import org.xhtmlrenderer.css.style.derived.RectPropertySet; import org.xhtmlrenderer.layout.Layer; import org.xhtmlrenderer.layout.LayoutContext; import org.xhtmlrenderer.layout.PaintingInfo; import org.xhtmlrenderer.layout.Styleable; import org.xhtmlrenderer.util.XRLog; public abstract class Box implements Styleable { protected static final String LINE_SEPARATOR = System.getProperty("line.separator"); private Element _element; private int _x; private int _y; private int _absY; private int _absX; /** * Box width. */ private int _contentWidth; private int _rightMBP = 0; private int _leftMBP = 0; /** * Box height. */ private int _height; private Layer _layer = null; private Layer _containingLayer; private Box _parent; private List _boxes; /** * Keeps track of the start of childrens containing block. */ private int _tx; private int _ty; private CalculatedStyle _style; private Box _containingBlock; private Dimension _relativeOffset; private PaintingInfo _paintingInfo; private RectPropertySet _workingMargin; private int _index; private String _pseudoElementOrClass; private boolean _anonymous; protected Box() { } public abstract String dump(LayoutContext c, String indent, int which); protected void dumpBoxes( LayoutContext c, String indent, List boxes, int which, StringBuffer result) { for (Iterator i = boxes.iterator(); i.hasNext(); ) { Box b = (Box)i.next(); result.append(b.dump(c, indent + " ", which)); if (i.hasNext()) { result.append('\n'); } } } public int getWidth() { return getContentWidth() + getLeftMBP() + getRightMBP(); } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Box: "); sb.append(" (" + getAbsX() + "," + getAbsY() + ")->(" + getWidth() + " x " + getHeight() + ")"); return sb.toString(); } public void addChildForLayout(LayoutContext c, Box child) { addChild(child); child.initContainingLayer(c); } public void addChild(Box child) { if (_boxes == null) { _boxes = new ArrayList(); } if (child == null) { throw new NullPointerException("trying to add null child"); } child.setParent(this); child.setIndex(_boxes.size()); _boxes.add(child); } public void addAllChildren(List children) { for (Iterator i = children.iterator(); i.hasNext(); ) { Box box = (Box)i.next(); addChild(box); } } public void removeAllChildren() { if (_boxes != null) { _boxes.clear(); } } public void removeChild(Box target) { if (_boxes != null) { boolean found = false; for (Iterator i = getChildIterator(); i.hasNext(); ) { Box child = (Box)i.next(); if (child.equals(target)) { i.remove(); found = true; } else if (found) { child.setIndex(child.getIndex()-1); } } } } public Box getPreviousSibling() { Box parent = getParent(); return parent == null ? null : parent.getPrevious(this); } public Box getNextSibling() { Box parent = getParent(); return parent == null ? null : parent.getNext(this); } protected Box getPrevious(Box child) { return child.getIndex() == 0 ? null : getChild(child.getIndex()-1); } protected Box getNext(Box child) { return child.getIndex() == getChildCount() - 1 ? null : getChild(child.getIndex()+1); } public void removeChild(int i) { if (_boxes != null) { removeChild(getChild(i)); } } public void setParent(Box box) { _parent = box; } public Box getParent() { return _parent; } public Box getDocumentParent() { return getParent(); } public int getChildCount() { return _boxes == null ? 0 : _boxes.size(); } public Box getChild(int i) { if (_boxes == null) { throw new IndexOutOfBoundsException(); } else { return (Box) _boxes.get(i); } } public Iterator getChildIterator() { return _boxes == null ? Collections.EMPTY_LIST.iterator() : _boxes.iterator(); } public List getChildren() { return _boxes == null ? Collections.EMPTY_LIST : _boxes; } public static final int NOTHING = 0; public static final int FLUX = 1; public static final int CHILDREN_FLUX = 2; public static final int DONE = 3; private int _state = NOTHING; public static final int DUMP_RENDER = 2; public static final int DUMP_LAYOUT = 1; public synchronized int getState() { return _state; } public synchronized void setState(int state) { _state = state; } public static String stateToString(int state) { switch (state) { case NOTHING: return "NOTHING"; case FLUX: return "FLUX"; case CHILDREN_FLUX: return "CHILDREN_FLUX"; case DONE: return "DONE"; default: return "unknown"; } } public final CalculatedStyle getStyle() { return _style; } public void setStyle(CalculatedStyle style) { _style = style; } public Box getContainingBlock() { return _containingBlock == null ? getParent() : _containingBlock; } public void setContainingBlock(Box containingBlock) { _containingBlock = containingBlock; } public Rectangle getMarginEdge(int left, int top, CssContext cssCtx, int tx, int ty) { // Note that negative margins can mean this rectangle is inside the border // edge, but that's the way it's supposed to work... Rectangle result = new Rectangle(left, top, getWidth(), getHeight()); result.translate(tx, ty); return result; } public Rectangle getMarginEdge(CssContext cssCtx, int tx, int ty) { return getMarginEdge(getX(), getY(), cssCtx, tx, ty); } public Rectangle getPaintingBorderEdge(CssContext cssCtx) { return getBorderEdge(getAbsX(), getAbsY(), cssCtx); } public Rectangle getPaintingPaddingEdge(CssContext cssCtx) { return getPaddingEdge(getAbsX(), getAbsY(), cssCtx); } public Rectangle getPaintingClipEdge(CssContext cssCtx) { return getPaintingBorderEdge(cssCtx); } public Rectangle getChildrenClipEdge(RenderingContext c) { return getPaintingPaddingEdge(c); } /** * <B>NOTE</B>: This method does not consider any children of this box */ public boolean intersects(CssContext cssCtx, Shape clip) { return clip == null || clip.intersects(getPaintingClipEdge(cssCtx)); } public Rectangle getBorderEdge(int left, int top, CssContext cssCtx) { RectPropertySet margin = getMargin(cssCtx); Rectangle result = new Rectangle(left + (int) margin.left(), top + (int) margin.top(), getWidth() - (int) margin.left() - (int) margin.right(), getHeight() - (int) margin.top() - (int) margin.bottom()); return result; } public Rectangle getPaddingEdge(int left, int top, CssContext cssCtx) { RectPropertySet margin = getMargin(cssCtx); RectPropertySet border = getBorder(cssCtx); Rectangle result = new Rectangle(left + (int) margin.left() + (int) border.left(), top + (int) margin.top() + (int) border.top(), getWidth() - (int) margin.width() - (int) border.width(), getHeight() - (int) margin.height() - (int) border.height()); return result; } protected int getPaddingWidth(CssContext cssCtx) { RectPropertySet padding = getPadding(cssCtx); return (int)padding.left() + getContentWidth() + (int)padding.right(); } public Rectangle getContentAreaEdge(int left, int top, CssContext cssCtx) { RectPropertySet margin = getMargin(cssCtx); RectPropertySet border = getBorder(cssCtx); RectPropertySet padding = getPadding(cssCtx); Rectangle result = new Rectangle( left + (int)margin.left() + (int)border.left() + (int)padding.left(), top + (int)margin.top() + (int)border.top() + (int)padding.top(), getWidth() - (int)margin.width() - (int)border.width() - (int)padding.width(), getHeight() - (int) margin.height() - (int) border.height() - (int) padding.height()); return result; } public Layer getLayer() { return _layer; } public void setLayer(Layer layer) { _layer = layer; } public Dimension positionRelative(CssContext cssCtx) { int initialX = getX(); int initialY = getY(); CalculatedStyle style = getStyle(); if (! style.isIdent(CSSName.LEFT, IdentValue.AUTO)) { setX(getX() + (int)style.getFloatPropertyProportionalWidth( CSSName.LEFT, getContainingBlock().getContentWidth(), cssCtx)); } else if (! style.isIdent(CSSName.RIGHT, IdentValue.AUTO)) { setX(getX() - (int)style.getFloatPropertyProportionalWidth( CSSName.RIGHT, getContainingBlock().getContentWidth(), cssCtx)); } int cbContentHeight = 0; if (! getContainingBlock().getStyle().isAutoHeight()) { CalculatedStyle cbStyle = getContainingBlock().getStyle(); cbContentHeight = (int)cbStyle.getFloatPropertyProportionalHeight( CSSName.HEIGHT, 0, cssCtx); } else if (isInlineBlock()) { // FIXME Should be content height, not overall height cbContentHeight = getContainingBlock().getHeight(); } if (!style.isIdent(CSSName.TOP, IdentValue.AUTO)) { setY(getY() + ((int)style.getFloatPropertyProportionalHeight( CSSName.TOP, cbContentHeight, cssCtx))); } else if (!style.isIdent(CSSName.BOTTOM, IdentValue.AUTO)) { setY(getY() - ((int)style.getFloatPropertyProportionalHeight( CSSName.BOTTOM, cbContentHeight, cssCtx))); } setRelativeOffset(new Dimension(getX() - initialX, getY() - initialY)); return getRelativeOffset(); } protected boolean isInlineBlock() { return false; } public void setAbsY(int absY) { _absY = absY; } public int getAbsY() { return _absY; } public void setAbsX(int absX) { _absX = absX; } public int getAbsX() { return _absX; } public boolean isStyled() { return _style != null; } public int getBorderSides() { return BorderPainter.ALL; } public void paintBorder(RenderingContext c) { c.getOutputDevice().paintBorder(c, this); } private boolean isPaintsRootElementBackground() { return (isRoot() && getStyle().isHasBackground()) || (isBody() && ! getParent().getStyle().isHasBackground()); } public void paintBackground(RenderingContext c) { if (! isPaintsRootElementBackground()) { c.getOutputDevice().paintBackground(c, this); } } public void paintRootElementBackground(RenderingContext c) { PaintingInfo pI = getPaintingInfo(); if (pI != null) { if (getStyle().isHasBackground()) { paintRootElementBackground(c, pI); } else if (getChildCount() > 0) { Box body = getChild(0); body.paintRootElementBackground(c, pI); } } } private void paintRootElementBackground(RenderingContext c, PaintingInfo pI) { Dimension marginCorner = pI.getOuterMarginCorner(); Rectangle canvasBounds = new Rectangle(0, 0, marginCorner.width, marginCorner.height); canvasBounds.add(c.getViewportRectangle()); c.getOutputDevice().paintBackground(c, getStyle(), canvasBounds, canvasBounds, null); } public Layer getContainingLayer() { return _containingLayer; } public void setContainingLayer(Layer containingLayer) { _containingLayer = containingLayer; } public void initContainingLayer(LayoutContext c) { if (getLayer() != null) { setContainingLayer(getLayer()); } else if (getContainingLayer() == null) { if (getParent() == null || getParent().getContainingLayer() == null) { throw new RuntimeException("internal error"); } setContainingLayer(getParent().getContainingLayer()); // FIXME Will be glacially slow for large inline relative layers. Could // be much more efficient. We're just looking for block boxes which are // directly wrapped by an inline relative layer (i.e. block boxes sandwiched // between anonymous block boxes) if (c.getLayer().isInline()) { List content = ((InlineLayoutBox)c.getLayer().getMaster()).getElementWithContent(); if (content.contains(this)) { setContainingLayer(c.getLayer()); } } } } public void connectChildrenToCurrentLayer(LayoutContext c) { for (int i = 0; i < getChildCount(); i++) { Box box = getChild(i); box.setContainingLayer(c.getLayer()); box.connectChildrenToCurrentLayer(c); } } public List getElementBoxes(Element elem) { List result = new ArrayList(); for (int i = 0; i < getChildCount(); i++) { Box child = getChild(i); if (child.getElement() == elem) { result.add(child); } result.addAll(child.getElementBoxes(elem)); } return result; } public void reset(LayoutContext c) { resetChildren(c); if (_layer != null) { _layer.detach(); _layer = null; } setContainingLayer(null); setLayer(null); setPaintingInfo(null); setContentWidth(0); _workingMargin = null; String anchorName = c.getNamespaceHandler().getAnchorName(getElement()); if (anchorName != null) { c.removeBoxId(anchorName); } Element e = getElement(); if (e != null) { String id = c.getNamespaceHandler().getID(e); if (id != null) { c.removeBoxId(id); } } } public void detach(LayoutContext c) { reset(c); if (getParent() != null) { getParent().removeChild(this); setParent(null); } } public void resetChildren(LayoutContext c, int start, int end) { for (int i = start; i <= end; i++) { Box box = getChild(i); box.reset(c); } } protected void resetChildren(LayoutContext c) { int remaining = getChildCount(); for (int i = 0; i < remaining; i++) { Box box = getChild(i); box.reset(c); } } public abstract void calcCanvasLocation(); public void calcChildLocations() { for (int i = 0; i < getChildCount(); i++) { Box child = getChild(i); child.calcCanvasLocation(); child.calcChildLocations(); } } public int forcePageBreakBefore(LayoutContext c, IdentValue pageBreakValue, boolean pendingPageName) { PageBox page = c.getRootLayer().getFirstPage(c, this); if (page == null) { XRLog.layout(Level.WARNING, "Box has no page"); return 0; } else { int pageBreakCount = 1; if (page.getTop() == getAbsY()) { pageBreakCount--; if (pendingPageName && page == c.getRootLayer().getLastPage()) { c.getRootLayer().removeLastPage(); c.setPageName(c.getPendingPageName()); c.getRootLayer().addPage(c); } } if ((page.isLeftPage() && pageBreakValue == IdentValue.LEFT) || (page.isRightPage() && pageBreakValue == IdentValue.RIGHT)) { pageBreakCount++; } if (pageBreakCount == 0) { return 0; } if (pageBreakCount == 1 && pendingPageName) { c.setPageName(c.getPendingPageName()); } int delta = page.getBottom() + c.getExtraSpaceTop() - getAbsY(); if (page == c.getRootLayer().getLastPage()) { c.getRootLayer().addPage(c); } if (pageBreakCount == 2) { page = (PageBox)c.getRootLayer().getPages().get(page.getPageNo()+1); delta += page.getContentHeight(c); if (pageBreakCount == 2 && pendingPageName) { c.setPageName(c.getPendingPageName()); } if (page == c.getRootLayer().getLastPage()) { c.getRootLayer().addPage(c); } } setY(getY() + delta); return delta; } } public void forcePageBreakAfter(LayoutContext c, IdentValue pageBreakValue) { boolean needSecondPageBreak = false; PageBox page = c.getRootLayer().getLastPage(c, this); if ((page.isLeftPage() && pageBreakValue == IdentValue.LEFT) || (page.isRightPage() && pageBreakValue == IdentValue.RIGHT)) { needSecondPageBreak = true; } int delta = page.getBottom() + c.getExtraSpaceTop() - (getAbsY() + getMarginBorderPadding(c, CalculatedStyle.TOP) + getHeight()); if (page == c.getRootLayer().getLastPage()) { c.getRootLayer().addPage(c); } if (needSecondPageBreak) { page = (PageBox)c.getRootLayer().getPages().get(page.getPageNo()+1); delta += page.getContentHeight(c); if (page == c.getRootLayer().getLastPage()) { c.getRootLayer().addPage(c); } } setHeight(getHeight() + delta); } public boolean crossesPageBreak(LayoutContext c) { if (! c.isPageBreaksAllowed()) { return false; } PageBox pageBox = c.getRootLayer().getFirstPage(c, this); if (pageBox == null) { return false; } else { return getAbsY() + getHeight() >= pageBox.getBottom() - c.getExtraSpaceBottom(); } } public Dimension getRelativeOffset() { return _relativeOffset; } public void setRelativeOffset(Dimension relativeOffset) { _relativeOffset = relativeOffset; } public Box find(CssContext cssCtx, int absX, int absY, boolean findAnonymous) { PaintingInfo pI = getPaintingInfo(); if (pI != null && ! pI.getAggregateBounds().contains(absX, absY)) { return null; } Box result = null; for (int i = 0; i < getChildCount(); i++) { Box child = getChild(i); result = child.find(cssCtx, absX, absY, findAnonymous); if (result != null) { return result; } } Rectangle edge = getContentAreaEdge(getAbsX(), getAbsY(), cssCtx); return edge.contains(absX, absY) && getStyle().isVisible() ? this : null; } public boolean isRoot() { return getElement() != null && ! isAnonymous() && getElement().getParentNode().getNodeType() == Node.DOCUMENT_NODE; } public boolean isBody() { return getParent() != null && getParent().isRoot(); } public Element getElement() { return _element; } public void setElement(Element element) { _element = element; } public void setMarginTop(CssContext cssContext, int marginTop) { ensureWorkingMargin(cssContext); _workingMargin.setTop(marginTop); } public void setMarginBottom(CssContext cssContext, int marginBottom) { ensureWorkingMargin(cssContext); _workingMargin.setBottom(marginBottom); } public void setMarginLeft(CssContext cssContext, int marginLeft) { ensureWorkingMargin(cssContext); _workingMargin.setLeft(marginLeft); } public void setMarginRight(CssContext cssContext, int marginRight) { ensureWorkingMargin(cssContext); _workingMargin.setRight(marginRight); } private void ensureWorkingMargin(CssContext cssContext) { if (_workingMargin == null) { _workingMargin = getStyleMargin(cssContext).copyOf(); } } public RectPropertySet getMargin(CssContext cssContext) { return _workingMargin != null ? _workingMargin : getStyleMargin(cssContext); } protected RectPropertySet getStyleMargin(CssContext cssContext) { return getStyle().getMarginRect(getContainingBlockWidth(), cssContext); } protected RectPropertySet getStyleMargin(CssContext cssContext, boolean useCache) { return getStyle().getMarginRect(getContainingBlockWidth(), cssContext, useCache); } public RectPropertySet getPadding(CssContext cssCtx) { return getStyle().getPaddingRect(getContainingBlockWidth(), cssCtx); } public BorderPropertySet getBorder(CssContext cssCtx) { return getStyle().getBorder(cssCtx); } protected int getContainingBlockWidth() { return getContainingBlock().getContentWidth(); } protected void resetTopMargin(CssContext cssContext) { if (_workingMargin != null) { RectPropertySet styleMargin = getStyleMargin(cssContext); _workingMargin.setTop(styleMargin.top()); } } public void clearSelection(List modified) { for (int i = 0; i < getChildCount(); i++) { Box child = (Box)getChild(i); child.clearSelection(modified); } } public void selectAll() { for (int i = 0; i < getChildCount(); i++) { Box child = (Box) getChild(i); child.selectAll(); } } public PaintingInfo calcPaintingInfo(CssContext c, boolean useCache) { PaintingInfo cached = getPaintingInfo(); if (cached != null && useCache) { return cached; } final PaintingInfo result = new PaintingInfo(); Rectangle bounds = getMarginEdge(getAbsX(), getAbsY(), c, 0, 0); result.setOuterMarginCorner( new Dimension(bounds.x + bounds.width, bounds.y + bounds.height)); result.setAggregateBounds(getPaintingClipEdge(c)); calcChildPaintingInfo(c, result, useCache); setPaintingInfo(result); return result; } protected void calcChildPaintingInfo( CssContext c, PaintingInfo result, boolean useCache) { for (int i = 0; i < getChildCount(); i++) { Box child = (Box) getChild(i); PaintingInfo info = child.calcPaintingInfo(c, useCache); moveIfGreater(result.getOuterMarginCorner(), info.getOuterMarginCorner()); result.getAggregateBounds().add(info.getAggregateBounds()); } } public int getMarginBorderPadding(CssContext cssCtx, int which) { BorderPropertySet border = getBorder(cssCtx); RectPropertySet margin = getMargin(cssCtx); RectPropertySet padding = getPadding(cssCtx); switch (which) { case CalculatedStyle.LEFT: return (int)(margin.left() + border.left() + padding.left()); case CalculatedStyle.RIGHT: return (int)(margin.right() + border.right() + padding.right()); case CalculatedStyle.TOP: return (int)(margin.top() + border.top() + padding.top()); case CalculatedStyle.BOTTOM: return (int)(margin.bottom() + border.bottom() + padding.bottom()); default: throw new IllegalArgumentException(); } } protected void moveIfGreater(Dimension result, Dimension test) { if (test.width > result.width) { result.width = test.width; } if (test.height > result.height) { result.height = test.height; } } public void restyle(LayoutContext c) { Element e = getElement(); CalculatedStyle style = null; String pe = getPseudoElementOrClass(); if (pe != null) { if (e != null) { style = c.getSharedContext().getStyle(e, true); style = style.deriveStyle(c.getCss().getPseudoElementStyle(e, pe)); } else { BlockBox container = (BlockBox)getParent().getParent(); e = container.getElement(); style = c.getSharedContext().getStyle(e, true); style = style.deriveStyle(c.getCss().getPseudoElementStyle(e, pe)); style = style.createAnonymousStyle(IdentValue.INLINE); } } else { if (e != null) { style = c.getSharedContext().getStyle(e, true); if (isAnonymous()) { style = style.createAnonymousStyle(getStyle().getIdent(CSSName.DISPLAY)); } } else { Box parent = getParent(); if (parent != null) { e = parent.getElement(); if (e != null) { style = c.getSharedContext().getStyle(e, true); style = style.createAnonymousStyle(IdentValue.INLINE); } } } } if (style != null) { setStyle(style); } restyleChildren(c); } protected void restyleChildren(LayoutContext c) { for (int i = 0; i < getChildCount(); i++) { Box b = getChild(i); b.restyle(c); } } public Box getRestyleTarget() { return this; } protected int getIndex() { return _index; } protected void setIndex(int index) { _index = index; } public String getPseudoElementOrClass() { return _pseudoElementOrClass; } public void setPseudoElementOrClass(String pseudoElementOrClass) { _pseudoElementOrClass = pseudoElementOrClass; } public void setX(int x) { _x = x; } public int getX() { return _x; } public void setY(int y) { _y = y; } public int getY() { return _y; } public void setTy(int ty) { _ty = ty; } public int getTy() { return _ty; } public void setTx(int tx) { _tx = tx; } public int getTx() { return _tx; } public void setRightMBP(int rightMBP) { _rightMBP = rightMBP; } public int getRightMBP() { return _rightMBP; } public void setLeftMBP(int leftMBP) { _leftMBP = leftMBP; } public int getLeftMBP() { return _leftMBP; } public void setHeight(int height) { _height = height; } public int getHeight() { return _height; } public void setContentWidth(int contentWidth) { _contentWidth = contentWidth; } public int getContentWidth() { return _contentWidth; } public PaintingInfo getPaintingInfo() { return _paintingInfo; } private void setPaintingInfo(PaintingInfo paintingInfo) { _paintingInfo = paintingInfo; } public boolean isAnonymous() { return _anonymous; } public void setAnonymous(boolean anonymous) { _anonymous = anonymous; } public BoxDimensions getBoxDimensions() { BoxDimensions result = new BoxDimensions(); result.setLeftMBP(getLeftMBP()); result.setRightMBP(getRightMBP()); result.setContentWidth(getContentWidth()); result.setHeight(getHeight()); return result; } public void setBoxDimensions(BoxDimensions dimensions) { setLeftMBP(dimensions.getLeftMBP()); setRightMBP(dimensions.getRightMBP()); setContentWidth(dimensions.getContentWidth()); setHeight(dimensions.getHeight()); } public void collectText(RenderingContext c, StringBuffer buffer) throws IOException { for (Iterator i = getChildIterator(); i.hasNext(); ) { Box b = (Box)i.next(); b.collectText(c, buffer); } } public void exportText(RenderingContext c, Writer writer) throws IOException { if (c.isPrint() && isRoot()) { c.setPage(0, (PageBox)c.getRootLayer().getPages().get(0)); c.getPage().exportLeadingText(c, writer); } for (Iterator i = getChildIterator(); i.hasNext(); ) { Box b = (Box)i.next(); b.exportText(c, writer); } if (c.isPrint() && isRoot()) { exportPageBoxText(c, writer); } } private void exportPageBoxText(RenderingContext c, Writer writer) throws IOException { c.getPage().exportTrailingText(c, writer); if (c.getPage() != c.getRootLayer().getLastPage()) { List pages = c.getRootLayer().getPages(); do { PageBox next = (PageBox)pages.get(c.getPageNo()+1); c.setPage(next.getPageNo(), next); next.exportLeadingText(c, writer); next.exportTrailingText(c, writer); } while (c.getPage() != c.getRootLayer().getLastPage()); } } protected void exportPageBoxText(RenderingContext c, Writer writer, int yPos) throws IOException { c.getPage().exportTrailingText(c, writer); List pages = c.getRootLayer().getPages(); PageBox next = (PageBox)pages.get(c.getPageNo()+1); c.setPage(next.getPageNo(), next); while (next.getBottom() < yPos) { next.exportLeadingText(c, writer); next.exportTrailingText(c, writer); next = (PageBox)pages.get(c.getPageNo()+1); c.setPage(next.getPageNo(), next); } next.exportLeadingText(c, writer); } public boolean isInDocumentFlow() { Box flowRoot = this; while (true) { Box parent = flowRoot.getParent(); if (parent == null) { break; } else { flowRoot = (BlockBox)parent; } } return flowRoot.isRoot(); } public void analyzePageBreaks(LayoutContext c, ContentLimitContainer container) { container.updateTop(c, getAbsY()); for (Iterator i = getChildIterator(); i.hasNext(); ) { Box b = (Box)i.next(); b.analyzePageBreaks(c, container); } container.updateBottom(c, getAbsY() + getHeight()); } public FSColor getEffBackgroundColor(RenderingContext c) { FSColor result = null; Box current = this; while (current != null) { result = current.getStyle().getBackgroundColor(); if (result != null) { return result; } current = current.getContainingBlock(); } PageBox page = c.getPage(); result = page.getStyle().getBackgroundColor(); if (result == null) { return new FSRGBColor(255, 255, 255); } else { return result; } } protected boolean isMarginAreaRoot() { return false; } public boolean isContainedInMarginBox() { Box current = this; while (true) { Box parent = current.getParent(); if (parent == null) { break; } else { current = parent; } } return current.isMarginAreaRoot(); } public int getEffectiveWidth() { return getWidth(); } } /* * $Id: Box.java,v 1.147 2010/01/11 01:27:41 peterbrant Exp $ * * $Log: Box.java,v $ * Revision 1.147 2010/01/11 01:27:41 peterbrant * Correct background-position calculation. Detective work and initial patch from Stefano Bagnara. * * 14.2 says backgrounds are painted over the border box. However, background-position is calculated relative to the padding box (spec 14.2.1). We were calculating it relative to the border box. * * Revision 1.146 2008/11/07 18:34:33 peterbrant * Add API to retrieve PDF page and coordinates for boxes with an ID attribute * * Revision 1.145 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.144 2008/04/30 23:02:42 peterbrant * Fix for bug #239 (detective work by Ludovic Durand-Texte) * * Revision 1.143 2008/01/26 15:34:52 peterbrant * Make getNextSibling() and getPreviousSibling() work with InlineLayoutBox (or as well as it can in that context) * * Revision 1.142 2007/08/23 20:52:31 peterbrant * Begin work on AcroForm support * Revision 1.141 2007/08/19 22:22:50 peterbrant Merge * R8pbrant changes to HEAD * * Revision 1.140.2.10 2007/08/18 00:52:44 peterbrant When paginating a table, * clip cells that span multiple pages to the effective content area visible on * the current page * * Revision 1.140.2.9 2007/08/15 21:29:31 peterbrant Initial draft of support * for running headers and footers on tables * * Revision 1.140.2.8 2007/08/14 16:10:30 peterbrant Remove obsolete code * * Revision 1.140.2.7 2007/08/13 22:57:03 peterbrant Make sure trailing pages * with only a header and footer are exported when exporting to text * * Revision 1.140.2.6 2007/08/13 22:41:13 peterbrant First pass at exporting the * render tree as text * * Revision 1.140.2.5 2007/08/09 20:18:16 peterbrant Bug fixes to improved * pagination support * * Revision 1.140.2.4 2007/08/08 18:28:25 peterbrant Further progress on CSS3 * paged media * * Revision 1.140.2.3 2007/08/07 17:06:32 peterbrant Implement named pages / * Implement page-break-before/after: left/right / Experiment with efficient * selection * * Revision 1.140.2.2 2007/07/30 00:43:15 peterbrant Start implementing text * selection and copying * * Revision 1.140.2.1 2007/07/09 22:18:03 peterbrant Begin work on running * headers and footers and named pages * * Revision 1.140 2007/06/11 22:30:15 peterbrant Minor cleanup to LayoutContext / * Start adding infrastructure to support better table pagination * * Revision 1.139 2007/04/25 18:09:41 peterbrant Always reset block box margin * if it is the first thing on a page * * Revision 1.138 2007/04/16 01:10:05 peterbrant Vertical margin and padding * with percentage values may be incorrect if box participated in a * shrink-to-fit calculation. Fix margin calculation. * * Revision 1.137 2007/04/15 00:34:40 peterbrant Allow inline-block / * inline-table content to be relatively positioned * * Revision 1.136 2007/03/17 22:55:51 peterbrant Remove distinction between box * IDs and named anchors * * Revision 1.135 2007/03/08 01:46:34 peterbrant Fix calculation of * viewport/page rectangle when calculating fixed background positions * * Revision 1.134 2007/02/24 01:57:30 peterbrant toString() changes * * Revision 1.133 2007/02/24 01:36:57 peterbrant Fix potential NPE if layout * fails * * Revision 1.132 2007/02/24 00:46:38 peterbrant Paint root element background * over entire canvas (or it's first child if the root element doesn't define a * background) * * Revision 1.131 2007/02/23 15:50:37 peterbrant Fix incorrect absolute box * positioning with print medium * * Revision 1.130 2007/02/22 18:21:19 peterbrant Add support for overflow: * visible/hidden * * Revision 1.129 2007/02/22 16:10:54 peterbrant Remove unused API * * Revision 1.128 2007/02/22 15:52:46 peterbrant Restyle generated content * correctly (although the CSS matcher needs more work before restyle with * generated content and dynamic pseudo classes will work) * * Revision 1.127 2007/02/22 15:30:42 peterbrant Internal links should be able * to target block boxes too (plus other minor cleanup) * * Revision 1.126 2007/02/21 23:49:41 peterbrant Can't calculate clearance until * margins have been collapsed / Clearance must be calculated relative to the * box's border edge, not margin edge * * Revision 1.125 2007/02/21 23:11:03 peterbrant Correct margin edge calculation * (as it turns out the straightforward approach is also the correct one) * * Revision 1.124 2007/02/21 19:15:05 peterbrant right and bottom need to push * the opposite direction as left and top with position: relative * * Revision 1.123 2007/02/19 23:42:54 peterbrant Fix resetChildren() typo * * Revision 1.122 2007/02/19 14:53:36 peterbrant Integrate new CSS parser * * Revision 1.121 2007/02/11 23:10:59 peterbrant Make sure bounds information is * calculated for fixed layers * * Revision 1.120 2007/02/07 16:33:22 peterbrant Initial commit of rewritten * table support and associated refactorings * * Revision 1.119 2006/10/04 23:52:57 peterbrant Implement support for margin: * auto (centering blocks in their containing block) * * Revision 1.118 2006/10/04 19:49:08 peterbrant Improve calculation of * available width when calculating shrink-to-fit width * * Revision 1.117 2006/09/08 15:41:58 peterbrant Calculate containing block * width accurately when collapsing margins / Provide collapsed bottom margin to * floats / Revive :first-line and :first-letter / Minor simplication in * InlineBoxing (get rid of now-superfluous InlineBoxInfo) * * Revision 1.116 2006/09/05 23:03:44 peterbrant Initial draft of shrink-to-fit * support * * Revision 1.115 2006/09/01 23:49:38 peterbrant Implement basic margin * collapsing / Various refactorings in preparation for shrink-to-fit / Add hack * to treat auto margins as zero * * Revision 1.114 2006/08/30 18:25:41 peterbrant Further refactoring / Bug fix * for problem reported by Mike Curtis * * Revision 1.113 2006/08/29 17:29:13 peterbrant Make Style object a thing of * the past * * Revision 1.112 2006/08/27 00:36:44 peterbrant Initial commit of (initial) R7 * work * * Revision 1.111 2006/03/01 00:45:02 peterbrant Provide LayoutContext when * calling detach() and friends * * Revision 1.110 2006/02/22 02:20:19 peterbrant Links and hover work again * * Revision 1.109 2006/02/21 20:43:45 peterbrant right was actually using left, * bottom was actually using top (relative positioning) * * Revision 1.108 2006/02/20 23:29:20 peterbrant Fix positioning of replaced * elements with margins, borders, padding * * Revision 1.107 2006/02/07 00:02:52 peterbrant If "keep together" cannot be * satisified, drop rule vs. pushing to next page / Fix bug with incorrect * positioning of content following relative block layers * * Revision 1.106 2006/02/01 01:30:14 peterbrant Initial commit of PDF work * * Revision 1.105 2006/01/27 01:15:38 peterbrant Start on better support for * different output devices * * Revision 1.104 2006/01/11 22:09:27 peterbrant toString() uses absolute * coordinates now * * Revision 1.103 2006/01/10 19:56:00 peterbrant Fix inappropriate box resizing * when width: auto * * Revision 1.102 2006/01/09 23:25:22 peterbrant Correct (?) position of debug * outline * * Revision 1.101 2006/01/04 19:50:14 peterbrant More pagination bug fixes / * Implement simple pagination for tables * * Revision 1.100 2006/01/03 23:55:57 peterbrant Add support for proper page * breaking of floats / More bug fixes to pagination support * * Revision 1.99 2006/01/03 02:12:20 peterbrant Various pagination fixes / Fix * fixed positioning * * Revision 1.98 2006/01/02 20:59:09 peterbrant Implement * page-break-before/after: avoid * * Revision 1.97 2006/01/01 03:14:25 peterbrant Implement page-break-inside: * avoid * * Revision 1.96 2006/01/01 02:38:19 peterbrant Merge more pagination work / * Various minor cleanups * * Revision 1.95 2005/12/30 01:32:39 peterbrant First merge of parts of * pagination work * * Revision 1.94 2005/12/28 00:50:52 peterbrant Continue ripping out first try * at pagination / Minor method name refactoring * * Revision 1.93 2005/12/21 02:36:29 peterbrant - Calculate absolute positions * incrementally (prep work for pagination) - Light cleanup - Fix bug where * floats nested in floats could cause the outer float to be positioned in the * wrong place * * Revision 1.92 2005/12/17 02:24:14 peterbrant Remove last pieces of old (now * non-working) clip region checking / Push down handful of fields from Box to * BlockBox * * Revision 1.91 2005/12/15 20:04:47 peterbrant Implement visibility: hidden * * Revision 1.90 2005/12/14 22:06:47 peterbrant Fix NPE * * Revision 1.89 2005/12/13 02:41:33 peterbrant Initial implementation of * vertical-align: top/bottom (not done yet) / Minor cleanup and optimization * * Revision 1.88 2005/12/11 02:51:18 peterbrant Minor tweak (misread spec) * * Revision 1.87 2005/12/10 03:11:43 peterbrant Use margin edge not content edge * * Revision 1.86 2005/12/09 21:41:19 peterbrant Finish support for relative * inline layers * * Revision 1.85 2005/12/09 01:24:56 peterbrant Initial commit of relative * inline layers * * Revision 1.84 2005/12/08 02:21:26 peterbrant Fix positioning bug when CB of * absolute block is a relative block * * Revision 1.83 2005/12/07 03:14:20 peterbrant Fixes to final float position * when float BFC is not contained in the layer being positioned / Implement * 10.6.7 of the spec * * Revision 1.82 2005/12/07 00:33:12 peterbrant :first-letter and :first-line * works again * * Revision 1.81 2005/12/05 00:13:53 peterbrant Improve list-item support * (marker positioning is now correct) / Start support for relative inline * layers * * Revision 1.80 2005/11/29 02:37:24 peterbrant Make clear work again / Rip out * old pagination code * * Revision 1.79 2005/11/25 16:57:20 peterbrant Initial commit of inline content * refactoring * * Revision 1.78 2005/11/13 01:14:16 tobega Take into account the height of a * first-letter. Also attempt to line-break better with inline padding. * * Revision 1.77 2005/11/10 18:27:28 peterbrant Position absolute box correctly * when top: auto and bottom: auto. * * Revision 1.76 2005/11/10 01:55:16 peterbrant Further progress on layer work * * Revision 1.75 2005/11/09 18:41:28 peterbrant Fixes to vertical margin * collapsing in the presence of floats / Paint floats as layers * * Revision 1.74 2005/11/08 20:03:57 peterbrant Further progress on painting * order / improved positioning implementation * * Revision 1.73 2005/11/05 23:19:07 peterbrant Always add fixed layers to root * layer / If element has fixed background just note this on the root layer * instead of property in Box * * Revision 1.72 2005/11/05 18:45:06 peterbrant General cleanup / Remove * obsolete code * * Revision 1.71 2005/11/05 03:30:01 peterbrant Start work on painting order and * improved positioning implementation * * Revision 1.70 2005/11/03 17:58:41 peterbrant Float rewrite (still stomping * bugs, but demos work) * * Revision 1.69 2005/11/02 18:15:29 peterbrant First merge of Tobe's and my * stacking context work / Rework float code (not done yet) * * Revision 1.68 2005/10/30 22:06:15 peterbrant Only create child List if * necessary * * Revision 1.67 2005/10/29 22:31:01 tobega House-cleaning * * Revision 1.66 2005/10/27 00:09:02 tobega Sorted out Context into * RenderingContext and LayoutContext * * Revision 1.65 2005/10/18 20:57:05 tobega Patch from Peter Brant * * Revision 1.64 2005/10/15 23:39:18 tobega patch from Peter Brant * * Revision 1.63 2005/10/12 21:17:13 tobega patch from Peter Brant * * Revision 1.62 2005/10/08 17:40:21 tobega Patch from Peter Brant * * Revision 1.61 2005/10/06 03:20:22 tobega Prettier incremental rendering. Ran * into more trouble than expected and some creepy crawlies and a few pages * don't look right (forms.xhtml, splash.xhtml) * * Revision 1.60 2005/10/02 21:30:00 tobega Fixed a lot of concurrency (and * other) issues from incremental rendering. Also some house-cleaning. * * Revision 1.59 2005/09/30 04:58:05 joshy fixed garbage when showing a document * with a fixed positioned block * * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.58 2005/09/29 21:34:04 joshy minor updates to a lot of files. * pulling in more incremental rendering code. fixed another resize bug Issue * number: Obtained from: Submitted by: Reviewed by: * * Revision 1.57 2005/09/26 22:40:21 tobega Applied patch from Peter Brant * concerning margin collapsing * * Revision 1.56 2005/07/04 00:12:12 tobega text-align now works for table-cells * too (is done in render, not in layout) * * Revision 1.55 2005/06/16 07:24:51 tobega Fixed background image bug. Caching * images in browser. Enhanced LinkListener. Some house-cleaning, playing with * Idea's code inspection utility. * * Revision 1.54 2005/06/16 04:31:30 joshy added clear support to the box Issue * number: Obtained from: Submitted by: Reviewed by: * * Revision 1.53 2005/05/13 15:23:55 tobega Done refactoring box borders, margin * and padding. Hover is working again. * * Revision 1.52 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.51 2005/05/08 14:36:58 tobega Refactored away the need for having * a context in a CalculatedStyle * * Revision 1.50 2005/04/22 17:19:19 joshy resovled conflicts in Box Issue * number: Obtained from: Submitted by: Reviewed by: * * Revision 1.49 2005/04/21 18:16:08 tobega Improved handling of inline padding. * Also fixed first-line handling according to spec. * * Revision 1.48 2005/04/19 17:51:18 joshy fixed absolute positioning bug * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.47 2005/04/19 13:59:48 pdoubleya Added defaults for margin, * padding, border. * * Revision 1.46 2005/02/03 23:16:16 pdoubleya . * * Revision 1.45 2005/01/31 22:51:35 pdoubleya Added caching for * padding/margin/border calcs, plus alternate calls to get their totals (with * and without style available). Reformatted. * * Revision 1.44 2005/01/29 20:24:23 pdoubleya Clean/reformat code. Removed * commented blocks, checked copyright. * * Revision 1.43 2005/01/24 22:46:42 pdoubleya Added support for ident-checks * using IdentValue instead of string comparisons. * * Revision 1.42 2005/01/24 19:01:03 pdoubleya Mass checkin. Changed to use * references to CSSName, which now has a Singleton instance for each property, * everywhere property names were being used before. Removed commented code. * Cascaded and Calculated style now store properties in arrays rather than * maps, for optimization. * * Revision 1.41 2005/01/24 14:36:35 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.40 2005/01/16 18:50:05 tobega Re-introduced caching of styles, * which make hamlet and alice scroll nicely again. Background painting still * slow though. * * Revision 1.39 2005/01/09 15:22:50 tobega Prepared improved handling of * margins, borders and padding. * * Revision 1.38 2005/01/07 00:29:29 tobega Removed Content reference from Box * (mainly to reduce memory footprint). In the process stumbled over and cleaned * up some messy stuff. * * Revision 1.37 2005/01/05 23:15:09 tobega Got rid of some redundant code for * hover-styling * * Revision 1.36 2005/01/05 01:10:15 tobega Went wild with code analysis tool. * removed unused stuff. Lucky we have CVS... * * Revision 1.35 2005/01/02 01:00:09 tobega Started sketching in code for * handling replaced elements in the NamespaceHandler * * Revision 1.34 2004/12/29 12:57:27 tobega Trying to handle BFC:s right * * Revision 1.33 2004/12/28 02:15:19 tobega More cleaning. * * Revision 1.32 2004/12/28 01:48:24 tobega More cleaning. Magically, the * financial report demo is starting to look reasonable, without any effort * being put on it. * * Revision 1.31 2004/12/27 09:40:48 tobega Moved more styling to render stage. * Now inlines have backgrounds and borders again. * * Revision 1.30 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.29 2004/12/16 15:53:10 joshy fixes for absolute layout * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.28 2004/12/13 15:15:57 joshy fixed bug where inlines would pick up * parent styles when they aren't supposed to fixed extra Xx's in printed text * added conf boolean to turn on box outlines * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.27 2004/12/12 23:19:26 tobega Tried to get hover working. * Something happens, but not all that's supposed to happen. * * Revision 1.26 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.25 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.24 2004/12/11 18:18:11 tobega Still broken, won't even compile at * the moment. Working hard to fix it, though. Replace the StyleReference * interface with our only concrete implementation, it was a bother changing in * two places all the time. * * Revision 1.23 2004/12/10 06:51:04 tobega Shamefully, I must now check in * painfully broken code. Good news is that Layout is much nicer, and we also * handle :before and :after, and do :first-line better than before. Table stuff * must be brought into line, but most needed is to fix Render. IMO Render * should work with Boxes and Content. If Render goes for a node, that is wrong. * * Revision 1.22 2004/12/09 21:18:53 tobega precaution: code still works * * Revision 1.21 2004/12/09 18:00:05 joshy fixed hover bugs fixed li's not being * blocks bug * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.20 2004/12/05 05:22:36 joshy fixed NPEs in selection listener * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.19 2004/12/05 05:18:02 joshy made bullets be anti-aliased fixed * bug in link listener that caused NPEs * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.18 2004/12/05 00:48:59 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.17 2004/12/01 01:57:02 joshy more updates for float support. * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.16 2004/11/18 16:45:13 joshy improved the float code a bit. now * floats are automatically forced to be blocks * * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.15 2004/11/17 00:44:54 joshy fixed bug in the history manager * added cursor support to the link listener * * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.14 2004/11/15 15:20:39 joshy fixes for absolute layout * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.13 2004/11/12 20:25:18 joshy added hover support to the browser * created hover demo fixed bug with inline borders * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.12 2004/11/12 17:05:25 joshy support for fixed positioning Issue * number: Obtained from: Submitted by: Reviewed by: * * Revision 1.11 2004/11/09 02:04:23 joshy support for text-align: justify * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.10 2004/11/08 15:10:10 joshy added support for styling * :first-letter inline boxes updated the absolute positioning tests * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.9 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.8 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.7 2004/11/03 23:54:34 joshy added hamlet and tables to the browser * more support for absolute layout added absolute layout unit tests removed * more dead code and moved code into layout factory * * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.6 2004/11/03 15:17:05 joshy added intial support for absolute * positioning * * Issue number: Obtained from: Submitted by: Reviewed by: * * Revision 1.5 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. * * */