private static List<Rectangle> getBoxesAtYLevel(List<Rectangle> floatRendererAreas, float currY) { List<Rectangle> yLevelBoxes = new ArrayList<>(); for (Rectangle box : floatRendererAreas) { if (box.getBottom() + AbstractRenderer.EPS < currY && box.getTop() + AbstractRenderer.EPS >= currY) { yLevelBoxes.add(box); } } return yLevelBoxes; } }
private static List<Rectangle> getBoxesAtYLevel(List<Rectangle> floatRendererAreas, float currY) { List<Rectangle> yLevelBoxes = new ArrayList<>(); for (Rectangle box : floatRendererAreas) { if (box.getBottom() + AbstractRenderer.EPS < currY && box.getTop() + AbstractRenderer.EPS >= currY) { yLevelBoxes.add(box); } } return yLevelBoxes; } }
/** * Get the rectangle representation of the intersection between this rectangle and the passed rectangle * * @param rect the rectangle to find the intersection with * @return the intersection rectangle if the passed rectangles intersects with this rectangle, * a rectangle representing a line if the intersection is along an edge or * a rectangle representing a point if the intersection is a single point, * null otherwise */ public Rectangle getIntersection(Rectangle rect) { Rectangle result = null; //Calculate possible lower-left corner and upper-right corner float llx = Math.max(x, rect.x); float lly = Math.max(y, rect.y); float urx = Math.min(getRight(), rect.getRight()); float ury = Math.min(getTop(), rect.getTop()); //If width or height is non-negative, there is overlap and we can construct the intersection rectangle float width = urx - llx; float height = ury - lly; if (Float.compare(width, 0) >= 0 && Float.compare(height, 0) >= 0) { if (Float.compare(width, 0) < 0) width = 0; if (Float.compare(height, 0) < 0) height = 0; result = new Rectangle(llx, lly, width, height); } return result; }
protected List<Point> rectangleToPointsList(Rectangle rect) { List<Point> points = new ArrayList<>(); points.addAll(Arrays.asList(new Point(rect.getLeft(), rect.getBottom()), new Point(rect.getRight(), rect.getBottom()), new Point(rect.getRight(), rect.getTop()), new Point(rect.getLeft(), rect.getTop()))); return points; }
protected List<Point> rectangleToPointsList(Rectangle rect) { List<Point> points = new ArrayList<>(); points.addAll(Arrays.asList(new Point(rect.getLeft(), rect.getBottom()), new Point(rect.getRight(), rect.getBottom()), new Point(rect.getRight(), rect.getTop()), new Point(rect.getLeft(), rect.getTop()))); return points; }
private void applyRotation(PdfPage page) { Rectangle rectangle = page.getPageSizeWithRotation(); int rotation = page.getRotation(); switch (rotation) { case 90: concatMatrix(0, 1, -1, 0, rectangle.getTop(), 0); break; case 180: concatMatrix(-1, 0, 0, -1, rectangle.getRight(), rectangle.getTop()); break; case 270: concatMatrix(0, -1, 1, 0, 0, rectangle.getRight()); break; } }
private void adjustLineOnFloatPlaced(Rectangle layoutBox, int childPos, FloatPropertyValue kidFloatPropertyVal, Rectangle justPlacedFloatBox) { if (justPlacedFloatBox.getBottom() >= layoutBox.getTop() || justPlacedFloatBox.getTop() < layoutBox.getTop()) { return; } boolean ltr = true; // TODO handle it float floatWidth = justPlacedFloatBox.getWidth(); if (kidFloatPropertyVal.equals(FloatPropertyValue.LEFT)) { layoutBox.setWidth(layoutBox.getWidth() - floatWidth).moveRight(floatWidth); occupiedArea.getBBox().moveRight(floatWidth); if (ltr) { for (int i = 0; i < childPos; ++i) { IRenderer prevChild = childRenderers.get(i); if (!FloatingHelper.isRendererFloating(prevChild)) { prevChild.move(floatWidth, 0); } } } } else { layoutBox.setWidth(layoutBox.getWidth() - floatWidth); if (!ltr) { // TODO } } }
private void adjustLineOnFloatPlaced(Rectangle layoutBox, int childPos, FloatPropertyValue kidFloatPropertyVal, Rectangle justPlacedFloatBox) { if (justPlacedFloatBox.getBottom() >= layoutBox.getTop() || justPlacedFloatBox.getTop() < layoutBox.getTop()) { return; } boolean ltr = true; // TODO handle it float floatWidth = justPlacedFloatBox.getWidth(); if (kidFloatPropertyVal.equals(FloatPropertyValue.LEFT)) { layoutBox.setWidth(layoutBox.getWidth() - floatWidth).moveRight(floatWidth); occupiedArea.getBBox().moveRight(floatWidth); if (ltr) { for (int i = 0; i < childPos; ++i) { IRenderer prevChild = childRenderers.get(i); if (!FloatingHelper.isRendererFloating(prevChild)) { prevChild.move(floatWidth, 0); } } } } else { layoutBox.setWidth(layoutBox.getWidth() - floatWidth); if (!ltr) { // TODO } } }
static Float calculateLineShiftUnderFloats(List<Rectangle> floatRendererAreas, Rectangle layoutBox) { List<Rectangle> boxesAtYLevel = getBoxesAtYLevel(floatRendererAreas, layoutBox.getTop()); if (boxesAtYLevel.isEmpty()) { return null; } Rectangle[] lastLeftAndRightBoxes = findLastLeftAndRightBoxes(layoutBox, boxesAtYLevel); float left = lastLeftAndRightBoxes[0] != null ? lastLeftAndRightBoxes[0].getRight() : layoutBox.getLeft(); float right = lastLeftAndRightBoxes[1] != null ? lastLeftAndRightBoxes[1].getLeft() : layoutBox.getRight(); if (layoutBox.getLeft() < left || layoutBox.getRight() > right) { float maxLastFloatBottom; if (lastLeftAndRightBoxes[0] != null && lastLeftAndRightBoxes[1] != null) { maxLastFloatBottom = Math.max(lastLeftAndRightBoxes[0].getBottom(), lastLeftAndRightBoxes[1].getBottom()); } else if (lastLeftAndRightBoxes[0] != null) { maxLastFloatBottom = lastLeftAndRightBoxes[0].getBottom(); } else { maxLastFloatBottom = lastLeftAndRightBoxes[1].getBottom(); } return layoutBox.getTop() - maxLastFloatBottom + AbstractRenderer.EPS; } return null; }
static Float calculateLineShiftUnderFloats(List<Rectangle> floatRendererAreas, Rectangle layoutBox) { List<Rectangle> boxesAtYLevel = getBoxesAtYLevel(floatRendererAreas, layoutBox.getTop()); if (boxesAtYLevel.isEmpty()) { return null; } Rectangle[] lastLeftAndRightBoxes = findLastLeftAndRightBoxes(layoutBox, boxesAtYLevel); float left = lastLeftAndRightBoxes[0] != null ? lastLeftAndRightBoxes[0].getRight() : layoutBox.getLeft(); float right = lastLeftAndRightBoxes[1] != null ? lastLeftAndRightBoxes[1].getLeft() : layoutBox.getRight(); if (layoutBox.getLeft() < left || layoutBox.getRight() > right) { float maxLastFloatBottom; if (lastLeftAndRightBoxes[0] != null && lastLeftAndRightBoxes[1] != null) { maxLastFloatBottom = Math.max(lastLeftAndRightBoxes[0].getBottom(), lastLeftAndRightBoxes[1].getBottom()); } else if (lastLeftAndRightBoxes[0] != null) { maxLastFloatBottom = lastLeftAndRightBoxes[0].getBottom(); } else { maxLastFloatBottom = lastLeftAndRightBoxes[1].getBottom(); } return layoutBox.getTop() - maxLastFloatBottom + AbstractRenderer.EPS; } return null; }
/** * Create a new PdfArray. The array is filled with the four values of the Rectangle in the * following order: left, bottom, right, top. * * @param rectangle Rectangle whose 4 values will be added to the PdfArray */ public PdfArray(Rectangle rectangle) { list = new ArrayList<>(4); add(new PdfNumber(rectangle.getLeft())); add(new PdfNumber(rectangle.getBottom())); add(new PdfNumber(rectangle.getRight())); add(new PdfNumber(rectangle.getTop())); }
private void updateMinHeightForAbsolutelyPositionedRenderer(IRenderer renderer, Rectangle parentRendererBox, Float top, Float bottom) { if (top != null && bottom != null && !renderer.hasProperty(Property.HEIGHT)) { UnitValue currentMaxHeight = getPropertyAsUnitValue(renderer, Property.MAX_HEIGHT); UnitValue currentMinHeight = getPropertyAsUnitValue(renderer, Property.MIN_HEIGHT); float resolvedMinHeight = Math.max(0, parentRendererBox.getTop() - (float) top - parentRendererBox.getBottom() - (float) bottom); Rectangle dummy = new Rectangle(0, 0); if (!isBorderBoxSizing(renderer)) { applyPaddings(dummy, getPaddings(renderer), true); applyBorderBox(dummy, getBorders(renderer), true); } applyMargins(dummy, getMargins(renderer), true); resolvedMinHeight -= dummy.getHeight(); if (currentMinHeight != null) { resolvedMinHeight = Math.max(resolvedMinHeight, currentMinHeight.getValue()); } if (currentMaxHeight != null) { resolvedMinHeight = Math.min(resolvedMinHeight, currentMaxHeight.getValue()); } renderer.setProperty(Property.MIN_HEIGHT, UnitValue.createPointValue((float) resolvedMinHeight)); } }
private void updateMinHeightForAbsolutelyPositionedRenderer(IRenderer renderer, Rectangle parentRendererBox, Float top, Float bottom) { if (top != null && bottom != null && !renderer.hasProperty(Property.HEIGHT)) { UnitValue currentMaxHeight = getPropertyAsUnitValue(renderer, Property.MAX_HEIGHT); UnitValue currentMinHeight = getPropertyAsUnitValue(renderer, Property.MIN_HEIGHT); float resolvedMinHeight = Math.max(0, parentRendererBox.getTop() - (float) top - parentRendererBox.getBottom() - (float) bottom); Rectangle dummy = new Rectangle(0, 0); if (!isBorderBoxSizing(renderer)) { applyPaddings(dummy, getPaddings(renderer), true); applyBorderBox(dummy, getBorders(renderer), true); } applyMargins(dummy, getMargins(renderer), true); resolvedMinHeight -= dummy.getHeight(); if (currentMinHeight != null) { resolvedMinHeight = Math.max(resolvedMinHeight, currentMinHeight.getValue()); } if (currentMaxHeight != null) { resolvedMinHeight = Math.min(resolvedMinHeight, currentMaxHeight.getValue()); } renderer.setProperty(Property.MIN_HEIGHT, UnitValue.createPointValue((float) resolvedMinHeight)); } }
static LayoutArea adjustResultOccupiedAreaForFloatAndClear(IRenderer renderer, List<Rectangle> floatRendererAreas, Rectangle parentBBox, float clearHeightCorrection, boolean marginsCollapsingEnabled) { LayoutArea occupiedArea = renderer.getOccupiedArea(); LayoutArea editedArea = occupiedArea; if (isRendererFloating(renderer)) { editedArea = occupiedArea.clone(); if (occupiedArea.getBBox().getWidth() > 0) { floatRendererAreas.add(occupiedArea.getBBox()); } editedArea.getBBox().setY(parentBBox.getTop()); editedArea.getBBox().setHeight(0); } else if (clearHeightCorrection > 0 && !marginsCollapsingEnabled) { editedArea = occupiedArea.clone(); editedArea.getBBox().increaseHeight(clearHeightCorrection); } return editedArea; }
protected void applyAbsolutePosition(Rectangle parentRect) { Float top = this.getPropertyAsFloat(Property.TOP); Float bottom = this.getPropertyAsFloat(Property.BOTTOM); Float left = this.getPropertyAsFloat(Property.LEFT); Float right = this.getPropertyAsFloat(Property.RIGHT); if (left == null && right == null && BaseDirection.RIGHT_TO_LEFT.equals(this.<BaseDirection>getProperty(Property.BASE_DIRECTION))) { right = 0f; } if (top == null && bottom == null) { top = 0f; } try { if (right != null) { move(parentRect.getRight() - (float) right - occupiedArea.getBBox().getRight(), 0); } if (left != null) { move(parentRect.getLeft() + (float) left - occupiedArea.getBBox().getLeft(), 0); } if (top != null) { move(0, parentRect.getTop() - (float) top - occupiedArea.getBBox().getTop()); } if (bottom != null) { move(0, parentRect.getBottom() + (float) bottom - occupiedArea.getBBox().getBottom()); } } catch (Exception exc) { Logger logger = LoggerFactory.getLogger(AbstractRenderer.class); logger.error(MessageFormatUtil.format(LogMessageConstant.OCCUPIED_AREA_HAS_NOT_BEEN_INITIALIZED, "Absolute positioning might be applied incorrectly.")); } }
static LayoutArea adjustResultOccupiedAreaForFloatAndClear(IRenderer renderer, List<Rectangle> floatRendererAreas, Rectangle parentBBox, float clearHeightCorrection, boolean marginsCollapsingEnabled) { LayoutArea occupiedArea = renderer.getOccupiedArea(); LayoutArea editedArea = occupiedArea; if (isRendererFloating(renderer)) { editedArea = occupiedArea.clone(); if (occupiedArea.getBBox().getWidth() > 0) { floatRendererAreas.add(occupiedArea.getBBox()); } editedArea.getBBox().setY(parentBBox.getTop()); editedArea.getBBox().setHeight(0); } else if (clearHeightCorrection > 0 && !marginsCollapsingEnabled) { editedArea = occupiedArea.clone(); editedArea.getBBox().increaseHeight(clearHeightCorrection); } return editedArea; }
private static void addOutline(List<Node> nodes, PdfOutline root, PdfDocument pdfDocument) { for (Node node : nodes) { PdfOutline child = root.addOutline(node.getTitle()); child.addAction(PdfAction.createGoTo( PdfExplicitDestination.createFitH(pdfDocument.getPage(node.getPage()), pdfDocument.getPage(node.getPage()).getPageSize().getTop()))); addOutline(node.getChildren(), child, pdfDocument); } }
private static void addOutline(List<Node> nodes, PdfOutline root, PdfDocument pdfDocument) { for (Node node : nodes) { PdfOutline child = root.addOutline(node.getTitle()); child.addAction(PdfAction.createGoTo( PdfExplicitDestination.createFitH(pdfDocument.getPage(node.getPage()), pdfDocument.getPage(node.getPage()).getPageSize().getTop()))); addOutline(node.getChildren(), child, pdfDocument); } }
/** * This method creates {@link AffineTransform} instance that could be used * to rotate content inside the occupied area. Be aware that it should be used only after * layout rendering is finished and correct occupied area for the rotated element is calculated. * * @return {@link AffineTransform} that rotates the content and places it inside occupied area. */ protected AffineTransform createRotationTransformInsideOccupiedArea() { Float angle = this.<Float>getProperty(Property.ROTATION_ANGLE); AffineTransform rotationTransform = AffineTransform.getRotateInstance((float) angle); Rectangle contentBox = this.getOccupiedAreaBBox(); List<Point> rotatedContentBoxPoints = transformPoints(rectangleToPointsList(contentBox), rotationTransform); // Occupied area for rotated elements is already calculated on layout in such way to enclose rotated content; // therefore we can simply rotate content as is and then shift it to the occupied area. float[] shift = calculateShiftToPositionBBoxOfPointsAt(occupiedArea.getBBox().getLeft(), occupiedArea.getBBox().getTop(), rotatedContentBoxPoints); rotationTransform.preConcatenate(AffineTransform.getTranslateInstance(shift[0], shift[1])); return rotationTransform; }
/** * This method creates {@link AffineTransform} instance that could be used * to rotate content inside the occupied area. Be aware that it should be used only after * layout rendering is finished and correct occupied area for the rotated element is calculated. * * @return {@link AffineTransform} that rotates the content and places it inside occupied area. */ protected AffineTransform createRotationTransformInsideOccupiedArea() { Float angle = this.<Float>getProperty(Property.ROTATION_ANGLE); AffineTransform rotationTransform = AffineTransform.getRotateInstance((float) angle); Rectangle contentBox = this.getOccupiedAreaBBox(); List<Point> rotatedContentBoxPoints = transformPoints(rectangleToPointsList(contentBox), rotationTransform); // Occupied area for rotated elements is already calculated on layout in such way to enclose rotated content; // therefore we can simply rotate content as is and then shift it to the occupied area. float[] shift = calculateShiftToPositionBBoxOfPointsAt(occupiedArea.getBBox().getLeft(), occupiedArea.getBBox().getTop(), rotatedContentBoxPoints); rotationTransform.preConcatenate(AffineTransform.getTranslateInstance(shift[0], shift[1])); return rotationTransform; }