From 8103c1bb0bedfbf9775660b483e11d00a064a980 Mon Sep 17 00:00:00 2001 From: danfickle Date: Fri, 18 Dec 2020 21:52:52 +1100 Subject: [PATCH] #615 FloatManager cleanup while debugging float clearance issue --- .../layout/BlockFormattingContext.java | 1 + .../openhtmltopdf/layout/FloatManager.java | 218 +++++++++--------- .../render/FlowingColumnContainerBox.java | 8 +- .../issue-615-infinite-loop-break-word.html | 13 +- 4 files changed, 128 insertions(+), 112 deletions(-) diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/BlockFormattingContext.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/BlockFormattingContext.java index ebe747b5e..65f4c0e2a 100755 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/BlockFormattingContext.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/BlockFormattingContext.java @@ -84,6 +84,7 @@ public void clear(LayoutContext c, Box current) { getFloatManager().clear(c, this, current); } + @Override public String toString() { return "BlockFormattingContext: (" + _x + "," + _y + ")"; } diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/FloatManager.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/FloatManager.java index 4faace32f..36d7dc3fe 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/FloatManager.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/FloatManager.java @@ -23,8 +23,10 @@ import java.awt.Rectangle; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; +import java.util.stream.Stream; import com.openhtmltopdf.css.style.CssContext; import com.openhtmltopdf.render.BlockBox; @@ -37,55 +39,63 @@ * non-floated (block) boxes. */ public class FloatManager { - public static final int LEFT = 1; - public static final int RIGHT = 2; + public enum FloatDirection { + LEFT, + RIGHT; + } /* Lazily created for performance. */ private List _leftFloats = Collections.emptyList(); private List _rightFloats = Collections.emptyList(); - + private final Box _master; public FloatManager(Box master) { this._master = master; } - private List getAddableFloats(int direction) { + private List getAddableFloats(FloatDirection direction) { if (getFloats(direction).isEmpty()) { setFloats(direction, new ArrayList<>()); } return getFloats(direction); } - - private void setFloats(int direction, List list) { - if (direction == LEFT) { + + private void setFloats(FloatDirection direction, List list) { + if (direction == FloatDirection.LEFT) { _leftFloats = list; } else { + assert direction == FloatDirection.RIGHT; _rightFloats = list; } } - + public void floatBox(LayoutContext c, Layer layer, BlockFormattingContext bfc, BlockBox box) { if (box.getStyle().isFloatedLeft()) { - position(c, bfc, box, LEFT); - save(box, layer, bfc, LEFT); + position(c, bfc, box, FloatDirection.LEFT); + save(box, layer, bfc, FloatDirection.LEFT); } else if (box.getStyle().isFloatedRight()) { - position(c, bfc, box, RIGHT); - save(box, layer, bfc, RIGHT); + position(c, bfc, box, FloatDirection.RIGHT); + save(box, layer, bfc, FloatDirection.RIGHT); } } public void clear(CssContext cssCtx, BlockFormattingContext bfc, Box box) { if (box.getStyle().isClearLeft()) { - moveClear(cssCtx, bfc, box, getFloats(LEFT)); + moveClear(cssCtx, bfc, box, getFloats(FloatDirection.LEFT)); } if (box.getStyle().isClearRight()) { - moveClear(cssCtx, bfc, box, getFloats(RIGHT)); + moveClear(cssCtx, bfc, box, getFloats(FloatDirection.RIGHT)); } } - private void save(BlockBox current, Layer layer, BlockFormattingContext bfc, int direction) { + private void save( + BlockBox current, + Layer layer, + BlockFormattingContext bfc, + FloatDirection direction) { + Point p = bfc.getOffset(); getAddableFloats(direction).add(new BoxOffset(current, p.x, p.y)); layer.addFloat(current, bfc); @@ -96,7 +106,7 @@ private void save(BlockBox current, Layer layer, BlockFormattingContext bfc, int } private void position(CssContext cssCtx, BlockFormattingContext bfc, - BlockBox current, int direction) { + BlockBox current, FloatDirection direction) { moveAllTheWayOver(current, direction); alignToLastOpposingFloat(cssCtx, bfc, current, direction); @@ -115,25 +125,32 @@ private void position(CssContext cssCtx, BlockFormattingContext bfc, } if (current.getStyle().isCleared()) { - if (current.getStyle().isClearLeft() && direction == LEFT) { - moveAllTheWayOver(current, LEFT); - } else if (current.getStyle().isClearRight() && direction == RIGHT) { - moveAllTheWayOver(current, RIGHT); + if (current.getStyle().isClearLeft() && direction == FloatDirection.LEFT) { + moveAllTheWayOver(current, FloatDirection.LEFT); + } else if (current.getStyle().isClearRight() && direction == FloatDirection.RIGHT) { + moveAllTheWayOver(current, FloatDirection.RIGHT); } moveFloatBelow(cssCtx, bfc, current, getFloats(direction)); } } - - public List getFloats(int direction) { - return direction == LEFT ? _leftFloats : _rightFloats; + + public List getFloats(FloatDirection direction) { + return direction == FloatDirection.LEFT ? _leftFloats : _rightFloats; } - private List getOpposingFloats(int direction) { - return direction == LEFT ? _rightFloats : _leftFloats; + public Stream getFloatStream(FloatDirection direction) { + return getFloats(direction).stream(); } - private void alignToLastFloat(CssContext cssCtx, - BlockFormattingContext bfc, BlockBox current, int direction) { + private List getOpposingFloats(FloatDirection direction) { + return direction == FloatDirection.LEFT ? _rightFloats : _leftFloats; + } + + private void alignToLastFloat( + CssContext cssCtx, + BlockFormattingContext bfc, + BlockBox current, + FloatDirection direction) { List floats = getFloats(direction); if (floats.size() > 0) { @@ -157,9 +174,9 @@ private void alignToLastFloat(CssContext cssCtx, } if (moveOver) { - if (direction == LEFT) { + if (direction == FloatDirection.LEFT) { currentBounds.x = lastBounds.x + last.getWidth(); - } else if (direction == RIGHT) { + } else if (direction == FloatDirection.RIGHT) { currentBounds.x = lastBounds.x - current.getWidth(); } @@ -171,11 +188,12 @@ private void alignToLastFloat(CssContext cssCtx, } } - private void alignToLastOpposingFloat(CssContext cssCtx, - BlockFormattingContext bfc, BlockBox current, int direction) { + private void alignToLastOpposingFloat( + CssContext cssCtx, + BlockFormattingContext bfc, BlockBox current, FloatDirection direction) { List floats = getOpposingFloats(direction); - if (floats.size() > 0) { + if (!floats.isEmpty()) { Point offset = bfc.getOffset(); BoxOffset lastOffset = floats.get(floats.size() - 1); @@ -194,10 +212,10 @@ private void alignToLastOpposingFloat(CssContext cssCtx, } } - private void moveAllTheWayOver(BlockBox current, int direction) { - if (direction == LEFT) { + private void moveAllTheWayOver(BlockBox current, FloatDirection direction) { + if (direction == FloatDirection.LEFT) { current.setX(0); - } else if (direction == RIGHT) { + } else if (direction == FloatDirection.RIGHT) { current.setX(current.getContainingBlock().getContentWidth() - current.getWidth()); } } @@ -208,22 +226,17 @@ private boolean fitsInContainingBlock(BlockBox current) { } private int findLowestY(CssContext cssCtx, List floats) { - int result = 0; - - for (BoxOffset floater : floats) { - Rectangle bounds = floater.getBox().getMarginEdge( - cssCtx, -floater.getX(), -floater.getY()); - if (bounds.y + bounds.height > result) { - result = bounds.y + bounds.height; - } - } - - return result; + return floats + .stream() + .map(floater -> floater.getBox().getMarginEdge(cssCtx, -floater.getX(), -floater.getY())) + .mapToInt(bounds -> bounds.y + bounds.height) + .max() + .orElse(0); } public int getClearDelta(CssContext cssCtx, int bfcRelativeY) { - int lowestLeftY = findLowestY(cssCtx, getFloats(LEFT)); - int lowestRightY = findLowestY(cssCtx, getFloats(RIGHT)); + int lowestLeftY = findLowestY(cssCtx, getFloats(FloatDirection.LEFT)); + int lowestRightY = findLowestY(cssCtx, getFloats(FloatDirection.RIGHT)); int lowestY = Math.max(lowestLeftY, lowestRightY); @@ -235,21 +248,15 @@ private boolean overlaps(CssContext cssCtx, BlockFormattingContext bfc, Point offset = bfc.getOffset(); Rectangle bounds = current.getMarginEdge(cssCtx, -offset.x, -offset.y); - for (BoxOffset floater : floats) { - Rectangle floaterBounds = floater.getBox().getMarginEdge(cssCtx, - -floater.getX(), -floater.getY()); - - if (floaterBounds.intersects(bounds)) { - return true; - } - } - - return false; + return floats + .stream() + .map(floater -> floater.getBox().getMarginEdge(cssCtx, -floater.getX(), -floater.getY())) + .anyMatch(floaterBounds -> floaterBounds.intersects(bounds)); } private void moveFloatBelow(CssContext cssCtx, BlockFormattingContext bfc, Box current, List floats) { - if (floats.size() == 0) { + if (floats.isEmpty()) { return; } @@ -264,7 +271,7 @@ private void moveFloatBelow(CssContext cssCtx, BlockFormattingContext bfc, private void moveClear(CssContext cssCtx, BlockFormattingContext bfc, Box current, List floats) { - if (floats.size() == 0) { + if (floats.isEmpty()) { return; } @@ -287,8 +294,8 @@ private void moveClear(CssContext cssCtx, BlockFormattingContext bfc, } public void removeFloat(BlockBox floater) { - removeFloat(floater, getFloats(LEFT)); - removeFloat(floater, getFloats(RIGHT)); + removeFloat(floater, getFloats(FloatDirection.LEFT)); + removeFloat(floater, getFloats(FloatDirection.RIGHT)); } private void removeFloat(BlockBox floater, List floats) { @@ -302,8 +309,8 @@ private void removeFloat(BlockBox floater, List floats) { } public void calcFloatLocations() { - calcFloatLocations(getFloats(LEFT)); - calcFloatLocations(getFloats(RIGHT)); + calcFloatLocations(getFloats(FloatDirection.LEFT)); + calcFloatLocations(getFloats(FloatDirection.RIGHT)); } private void calcFloatLocations(List floats) { @@ -324,23 +331,11 @@ private void applyLineHeightHack(CssContext cssCtx, Box line, Rectangle bounds) public int getNextLineBoxDelta(CssContext cssCtx, BlockFormattingContext bfc, LineBox line, int containingBlockContentWidth) { - BoxDistance left = getFloatDistance(cssCtx, bfc, line, containingBlockContentWidth, getFloats(LEFT), LEFT); - BoxDistance right = getFloatDistance(cssCtx, bfc, line, containingBlockContentWidth, getFloats(RIGHT), RIGHT); - - int leftDelta; - int rightDelta; - - if (left.getBox() != null) { - leftDelta = calcDelta(cssCtx, line, left); - } else { - leftDelta = 0; - } + BoxDistance left = getFloatDistance(cssCtx, bfc, line, containingBlockContentWidth, getFloats(FloatDirection.LEFT), FloatDirection.LEFT); + BoxDistance right = getFloatDistance(cssCtx, bfc, line, containingBlockContentWidth, getFloats(FloatDirection.RIGHT), FloatDirection.RIGHT); - if (right.getBox() != null) { - rightDelta = calcDelta(cssCtx, line, right); - } else { - rightDelta = 0; - } + int leftDelta = left.getBox() != null ? calcDelta(cssCtx, line, left) : 0; + int rightDelta = right.getBox() != null ? calcDelta(cssCtx, line, right) : 0; return Math.max(leftDelta, rightDelta); } @@ -354,18 +349,23 @@ private int calcDelta(CssContext cssCtx, LineBox line, BoxDistance boxDistance) public int getLeftFloatDistance(CssContext cssCtx, BlockFormattingContext bfc, LineBox line, int containingBlockContentWidth) { - return getFloatDistance(cssCtx, bfc, line, containingBlockContentWidth, getFloats(LEFT), LEFT).getDistance(); + return getFloatDistance(cssCtx, bfc, line, containingBlockContentWidth, getFloats(FloatDirection.LEFT), FloatDirection.LEFT).getDistance(); } public int getRightFloatDistance(CssContext cssCtx, BlockFormattingContext bfc, LineBox line, int containingBlockContentWidth) { - return getFloatDistance(cssCtx, bfc, line, containingBlockContentWidth, getFloats(RIGHT), RIGHT).getDistance(); + return getFloatDistance(cssCtx, bfc, line, containingBlockContentWidth, getFloats(FloatDirection.RIGHT), FloatDirection.RIGHT).getDistance(); } - private BoxDistance getFloatDistance(CssContext cssCtx, BlockFormattingContext bfc, - LineBox line, int containingBlockContentWidth, - List floatsList, int direction) { - if (floatsList.size() == 0) { + private BoxDistance getFloatDistance( + CssContext cssCtx, + BlockFormattingContext bfc, + LineBox line, + int containingBlockContentWidth, + List floatsList, + FloatDirection direction) { + + if (floatsList.isEmpty()) { return new BoxDistance(null, 0); } @@ -373,27 +373,30 @@ private BoxDistance getFloatDistance(CssContext cssCtx, BlockFormattingContext b Rectangle lineBounds = line.getMarginEdge(cssCtx, -offset.x, -offset.y); lineBounds.width = containingBlockContentWidth; - int farthestOver = direction == LEFT ? lineBounds.x : lineBounds.x + lineBounds.width; + int farthestOver = direction == FloatDirection.LEFT ? lineBounds.x : lineBounds.x + lineBounds.width; applyLineHeightHack(cssCtx, line, lineBounds); + BlockBox farthestOverBox = null; - for (int i = 0; i < floatsList.size(); i++) { - BoxOffset floater = floatsList.get(i); + + for (BoxOffset floater : floatsList) { Rectangle fr = floater.getBox().getMarginEdge(cssCtx, -floater.getX(), -floater.getY()); + if (lineBounds.intersects(fr)) { - if (direction == LEFT && fr.x + fr.width > farthestOver) { + if (direction == FloatDirection.LEFT && fr.x + fr.width > farthestOver) { farthestOver = fr.x + fr.width; - } else if (direction == RIGHT && fr.x < farthestOver) { + } else if (direction == FloatDirection.RIGHT && fr.x < farthestOver) { farthestOver = fr.x; } + farthestOverBox = floater.getBox(); } } - if (direction == LEFT) { + if (direction == FloatDirection.LEFT) { return new BoxDistance(farthestOverBox, farthestOver - lineBounds.x); } else { - return new BoxDistance(farthestOverBox,lineBounds.x + lineBounds.width - farthestOver); + return new BoxDistance(farthestOverBox, lineBounds.x + lineBounds.width - farthestOver); } } @@ -403,20 +406,15 @@ public Box getMaster() { public Point getOffset(BlockBox floater) { // FIXME inefficient (but probably doesn't matter) - return getOffset(floater, - floater.getStyle().isFloatedLeft() ? getFloats(LEFT) : getFloats(RIGHT)); + return getOffset(floater, + getFloatStream(floater.getStyle().isFloatedLeft() ? FloatDirection.LEFT : FloatDirection.RIGHT)); } - private Point getOffset(BlockBox floater, List floats) { - for (BoxOffset boxOffset : floats) { - BlockBox box = boxOffset.getBox(); - - if (box.equals(floater)) { - return new Point(boxOffset.getX(), boxOffset.getY()); - } - } - - return null; + private Point getOffset(BlockBox floater, Stream floats) { + return floats.filter(boxOffset -> boxOffset.getBox().equals(floater)) + .findFirst() + .map(boxOffset -> new Point(boxOffset.getX(), boxOffset.getY())) + .orElse(null); } private void performFloatOperation(FloatOperation op, List floats) { @@ -431,8 +429,8 @@ private void performFloatOperation(FloatOperation op, List floats) { } public void performFloatOperation(FloatOperation op) { - performFloatOperation(op, getFloats(LEFT)); - performFloatOperation(op, getFloats(RIGHT)); + performFloatOperation(op, getFloats(FloatDirection.LEFT)); + performFloatOperation(op, getFloats(FloatDirection.RIGHT)); } public static class BoxOffset { @@ -475,8 +473,14 @@ BlockBox getBox() { int getDistance() { return _distance; } + + @Override + public String toString() { + return "BoxDistance [_box=" + _box + ", _distance=" + _distance + "]"; + } } + @FunctionalInterface public interface FloatOperation { public void operate(Box floater); } diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/FlowingColumnContainerBox.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/FlowingColumnContainerBox.java index 88c12f824..a5b829935 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/FlowingColumnContainerBox.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/FlowingColumnContainerBox.java @@ -136,8 +136,8 @@ private void layoutFloats(TreeMap columns, List columnMap, PersistentBFC bfc, int columnCount, int colWidth, int colGap) { - List floatsL = this.getPersistentBFC().getFloatManager().getFloats(FloatManager.LEFT); - List floatsR = this.getPersistentBFC().getFloatManager().getFloats(FloatManager.RIGHT); + List floatsL = this.getPersistentBFC().getFloatManager().getFloats(FloatManager.FloatDirection.LEFT); + List floatsR = this.getPersistentBFC().getFloatManager().getFloats(FloatManager.FloatDirection.RIGHT); layoutFloats(columnMap, floatsL, columnCount, colWidth, colGap); layoutFloats(columnMap, floatsR, columnCount, colWidth, colGap); @@ -173,8 +173,8 @@ private int adjustUnbalanced(LayoutContext c, Box child, int colGap, int colWidt final List pages = c.getRootLayer().getPages(); final boolean haveFloats = - !this.getPersistentBFC().getFloatManager().getFloats(FloatManager.LEFT).isEmpty() || - !this.getPersistentBFC().getFloatManager().getFloats(FloatManager.RIGHT).isEmpty(); + !this.getPersistentBFC().getFloatManager().getFloats(FloatManager.FloatDirection.LEFT).isEmpty() || + !this.getPersistentBFC().getFloatManager().getFloats(FloatManager.FloatDirection.RIGHT).isEmpty(); // We only need the tree map if we have floats. final TreeMap columnMap = haveFloats ? new TreeMap<>() : null; diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-615-infinite-loop-break-word.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-615-infinite-loop-break-word.html index 83856f626..491c72ee9 100644 --- a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-615-infinite-loop-break-word.html +++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-615-infinite-loop-break-word.html @@ -1,6 +1,17 @@ + + + -
+

a