/** * Returns a <code>Shape</code> representing the outline of this * <code>TextLayout</code>. * @param tx an optional {@link AffineTransform} to apply to the * outline of this <code>TextLayout</code>. * @return a <code>Shape</code> that is the outline of this * <code>TextLayout</code>. This is in standard coordinates. */ public Shape getOutline(AffineTransform tx) { ensureCache(); Shape result = textLine.getOutline(tx); LayoutPathImpl lp = textLine.getLayoutPath(); if (lp != null) { result = lp.mapShape(result); } return result; }
/** * Returns a {@code Shape} representing the outline of this * {@code TextLayout}. * @param tx an optional {@link AffineTransform} to apply to the * outline of this {@code TextLayout}. * @return a {@code Shape} that is the outline of this * {@code TextLayout}. This is in standard coordinates. */ public Shape getOutline(AffineTransform tx) { ensureCache(); Shape result = textLine.getOutline(tx); LayoutPathImpl lp = textLine.getLayoutPath(); if (lp != null) { result = lp.mapShape(result); } return result; }
private static GeneralPath pathToShape(double[] path, boolean close, LayoutPathImpl lp) { GeneralPath result = new GeneralPath(GeneralPath.WIND_EVEN_ODD, path.length); result.moveTo((float)path[0], (float)path[1]); for (int i = 2; i < path.length; i += 2) { result.lineTo((float)path[i], (float)path[i+1]); } if (close) { result.closePath(); } if (lp != null) { result = (GeneralPath)lp.mapShape(result); } return result; }
/** * Returns two paths corresponding to the strong and weak caret. * @param offset an offset in this <code>TextLayout</code> * @param bounds the bounds to which to extend the carets. The * bounds is in baseline-relative coordinates. * @param policy the specified <code>CaretPolicy</code> * @return an array of two paths. Element zero is the strong * caret. If there are two carets, element one is the weak caret, * otherwise it is <code>null</code>. The returned shapes * are in standard coordinates. */ public Shape[] getCaretShapes(int offset, Rectangle2D bounds, CaretPolicy policy) { ensureCache(); if (offset < 0 || offset > characterCount) { throw new IllegalArgumentException("Offset out of bounds in TextLayout.getCaretShapes()"); } if (bounds == null) { throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getCaretShapes()"); } if (policy == null) { throw new IllegalArgumentException("Null CaretPolicy passed to TextLayout.getCaretShapes()"); } Shape[] result = new Shape[2]; TextHitInfo hit = TextHitInfo.afterOffset(offset); int hitCaret = hitToCaret(hit); LayoutPathImpl lp = textLine.getLayoutPath(); Shape hitShape = pathToShape(getCaretPath(hit, bounds), false, lp); TextHitInfo otherHit = hit.getOtherHit(); int otherCaret = hitToCaret(otherHit); if (hitCaret == otherCaret) { result[0] = hitShape; } else { // more than one caret Shape otherShape = pathToShape(getCaretPath(otherHit, bounds), false, lp); TextHitInfo strongHit = policy.getStrongCaret(hit, otherHit, this); boolean hitIsStrong = strongHit.equals(hit); if (hitIsStrong) {// then other is weak result[0] = hitShape; result[1] = otherShape; } else { result[0] = otherShape; result[1] = hitShape; } } return result; }
LayoutPathImpl getLayoutPath() { return lp; }
/** * Returns two paths corresponding to the strong and weak caret. * @param offset an offset in this {@code TextLayout} * @param bounds the bounds to which to extend the carets. The * bounds is in baseline-relative coordinates. * @param policy the specified {@code CaretPolicy} * @return an array of two paths. Element zero is the strong * caret. If there are two carets, element one is the weak caret, * otherwise it is {@code null}. The returned shapes * are in standard coordinates. */ public Shape[] getCaretShapes(int offset, Rectangle2D bounds, CaretPolicy policy) { ensureCache(); if (offset < 0 || offset > characterCount) { throw new IllegalArgumentException("Offset out of bounds in TextLayout.getCaretShapes()"); } if (bounds == null) { throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getCaretShapes()"); } if (policy == null) { throw new IllegalArgumentException("Null CaretPolicy passed to TextLayout.getCaretShapes()"); } Shape[] result = new Shape[2]; TextHitInfo hit = TextHitInfo.afterOffset(offset); int hitCaret = hitToCaret(hit); LayoutPathImpl lp = textLine.getLayoutPath(); Shape hitShape = pathToShape(getCaretPath(hit, bounds), false, lp); TextHitInfo otherHit = hit.getOtherHit(); int otherCaret = hitToCaret(otherHit); if (hitCaret == otherCaret) { result[0] = hitShape; } else { // more than one caret Shape otherShape = pathToShape(getCaretPath(otherHit, bounds), false, lp); TextHitInfo strongHit = policy.getStrongCaret(hit, otherHit, this); boolean hitIsStrong = strongHit.equals(hit); if (hitIsStrong) {// then other is weak result[0] = hitShape; result[1] = otherShape; } else { result[0] = otherShape; result[1] = hitShape; } } return result; }
/** * Returns a path enclosing the visual selection in the specified range, * extended to {@code bounds}. * <p> * If the selection includes the leftmost (topmost) position, the selection * is extended to the left (top) of {@code bounds}. If the * selection includes the rightmost (bottommost) position, the selection * is extended to the right (bottom) of the bounds. The height * (width on vertical lines) of the selection is always extended to * {@code bounds}. * <p> * Although the selection is always contiguous, the logically selected * text can be discontiguous on lines with mixed-direction text. The * logical ranges of text selected can be retrieved using * {@code getLogicalRangesForVisualSelection}. For example, * consider the text 'ABCdef' where capital letters indicate * right-to-left text, rendered on a right-to-left line, with a visual * selection from 0L (the leading edge of 'A') to 3T (the trailing edge * of 'd'). The text appears as follows, with bold underlined areas * representing the selection: * <br><pre> * d<u><b>efCBA </b></u> * </pre> * The logical selection ranges are 0-3, 4-6 (ABC, ef) because the * visually contiguous text is logically discontiguous. Also note that * since the rightmost position on the layout (to the right of 'A') is * selected, the selection is extended to the right of the bounds. * @param firstEndpoint one end of the visual selection * @param secondEndpoint the other end of the visual selection * @param bounds the bounding rectangle to which to extend the selection. * This is in baseline-relative coordinates. * @return a {@code Shape} enclosing the selection. This is in * standard coordinates. * @see #getLogicalRangesForVisualSelection(TextHitInfo, TextHitInfo) * @see #getLogicalHighlightShape(int, int, Rectangle2D) */ public Shape getVisualHighlightShape(TextHitInfo firstEndpoint, TextHitInfo secondEndpoint, Rectangle2D bounds) { ensureCache(); checkTextHit(firstEndpoint); checkTextHit(secondEndpoint); if(bounds == null) { throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getVisualHighlightShape()"); } GeneralPath result = new GeneralPath(GeneralPath.WIND_EVEN_ODD); int firstCaret = hitToCaret(firstEndpoint); int secondCaret = hitToCaret(secondEndpoint); result.append(caretBoundingShape(firstCaret, secondCaret, bounds), false); if (firstCaret == 0 || secondCaret == 0) { GeneralPath ls = leftShape(bounds); if (!ls.getBounds().isEmpty()) result.append(ls, false); } if (firstCaret == characterCount || secondCaret == characterCount) { GeneralPath rs = rightShape(bounds); if (!rs.getBounds().isEmpty()) { result.append(rs, false); } } LayoutPathImpl lp = textLine.getLayoutPath(); if (lp != null) { result = (GeneralPath)lp.mapShape(result); // dlf cast safe? } return result; }
/** * Returns the black box bounds of the characters in the specified range. * The black box bounds is an area consisting of the union of the bounding * boxes of all the glyphs corresponding to the characters between start * and limit. This area can be disjoint. * @param firstEndpoint one end of the character range * @param secondEndpoint the other end of the character range. Can be * less than {@code firstEndpoint}. * @return a {@code Shape} enclosing the black box bounds. This is * in standard coordinates. */ public Shape getBlackBoxBounds(int firstEndpoint, int secondEndpoint) { ensureCache(); if (firstEndpoint > secondEndpoint) { int t = firstEndpoint; firstEndpoint = secondEndpoint; secondEndpoint = t; } if (firstEndpoint < 0 || secondEndpoint > characterCount) { throw new IllegalArgumentException("Invalid range passed to TextLayout.getBlackBoxBounds()"); } /* * return an area that consists of the bounding boxes of all the * characters from firstEndpoint to limit */ GeneralPath result = new GeneralPath(GeneralPath.WIND_NON_ZERO); if (firstEndpoint < characterCount) { for (int logIndex = firstEndpoint; logIndex < secondEndpoint; logIndex++) { Rectangle2D r = textLine.getCharBounds(logIndex); if (!r.isEmpty()) { result.append(r, false); } } } if (dx != 0 || dy != 0) { AffineTransform tx = AffineTransform.getTranslateInstance(dx, dy); result = (GeneralPath)tx.createTransformedShape(result); } LayoutPathImpl lp = textLine.getLayoutPath(); if (lp != null) { result = (GeneralPath)lp.mapShape(result); } //return new Highlight(result, false); return result; }