/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.graphics; import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.SpannedString; import android.text.TextUtils; import java.awt.BasicStroke; import java.awt.Font; import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * A paint implementation overridden by the LayoutLib bridge. */ public class Paint extends _Original_Paint { private int mColor = 0xFFFFFFFF; private float mStrokeWidth = 1.f; private float mTextSize = 20; private float mScaleX = 1; private float mSkewX = 0; private Align mAlign = Align.LEFT; private Style mStyle = Style.FILL; private float mStrokeMiter = 4.0f; private Cap mCap = Cap.BUTT; private Join mJoin = Join.MITER; private int mFlags = 0; /** * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}. */ public static final class FontInfo { Font mFont; java.awt.FontMetrics mMetrics; } private List<FontInfo> mFonts; private final FontRenderContext mFontContext = new FontRenderContext( new AffineTransform(), true, true); public static final int ANTI_ALIAS_FLAG = _Original_Paint.ANTI_ALIAS_FLAG; public static final int FILTER_BITMAP_FLAG = _Original_Paint.FILTER_BITMAP_FLAG; public static final int DITHER_FLAG = _Original_Paint.DITHER_FLAG; public static final int UNDERLINE_TEXT_FLAG = _Original_Paint.UNDERLINE_TEXT_FLAG; public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG; public static final int FAKE_BOLD_TEXT_FLAG = _Original_Paint.FAKE_BOLD_TEXT_FLAG; public static final int LINEAR_TEXT_FLAG = _Original_Paint.LINEAR_TEXT_FLAG; public static final int SUBPIXEL_TEXT_FLAG = _Original_Paint.SUBPIXEL_TEXT_FLAG; public static final int DEV_KERN_TEXT_FLAG = _Original_Paint.DEV_KERN_TEXT_FLAG; public static class FontMetrics extends _Original_Paint.FontMetrics { } public static class FontMetricsInt extends _Original_Paint.FontMetricsInt { } /** * The Style specifies if the primitive being drawn is filled, * stroked, or both (in the same color). The default is FILL. */ public enum Style { /** * Geometry and text drawn with this style will be filled, ignoring all * stroke-related settings in the paint. */ FILL (0), /** * Geometry and text drawn with this style will be stroked, respecting * the stroke-related fields on the paint. */ STROKE (1), /** * Geometry and text drawn with this style will be both filled and * stroked at the same time, respecting the stroke-related fields on * the paint. */ FILL_AND_STROKE (2); Style(int nativeInt) { this.nativeInt = nativeInt; } final int nativeInt; } /** * The Cap specifies the treatment for the beginning and ending of * stroked lines and paths. The default is BUTT. */ public enum Cap { /** * The stroke ends with the path, and does not project beyond it. */ BUTT (0), /** * The stroke projects out as a square, with the center at the end * of the path. */ ROUND (1), /** * The stroke projects out as a semicircle, with the center at the * end of the path. */ SQUARE (2); private Cap(int nativeInt) { this.nativeInt = nativeInt; } final int nativeInt; /** custom for layoutlib */ public int getJavaCap() { switch (this) { case BUTT: return BasicStroke.CAP_BUTT; case ROUND: return BasicStroke.CAP_ROUND; default: case SQUARE: return BasicStroke.CAP_SQUARE; } } } /** * The Join specifies the treatment where lines and curve segments * join on a stroked path. The default is MITER. */ public enum Join { /** * The outer edges of a join meet at a sharp angle */ MITER (0), /** * The outer edges of a join meet in a circular arc. */ ROUND (1), /** * The outer edges of a join meet with a straight line */ BEVEL (2); private Join(int nativeInt) { this.nativeInt = nativeInt; } final int nativeInt; /** custom for layoutlib */ public int getJavaJoin() { switch (this) { default: case MITER: return BasicStroke.JOIN_MITER; case ROUND: return BasicStroke.JOIN_ROUND; case BEVEL: return BasicStroke.JOIN_BEVEL; } } } /** * Align specifies how drawText aligns its text relative to the * [x,y] coordinates. The default is LEFT. */ public enum Align { /** * The text is drawn to the right of the x,y origin */ LEFT (0), /** * The text is drawn centered horizontally on the x,y origin */ CENTER (1), /** * The text is drawn to the left of the x,y origin */ RIGHT (2); private Align(int nativeInt) { this.nativeInt = nativeInt; } final int nativeInt; } public Paint() { this(0); } /* * Do not remove or com.android.layoutlib.bridge.TestClassReplacement fails. */ @Override public void finalize() { } public Paint(int flags) { setFlags(flags | DEFAULT_PAINT_FLAGS); initFont(); } public Paint(Paint paint) { set(paint); initFont(); } @Override public void reset() { super.reset(); } /** * Returns the list of {@link Font} objects. The first item is the main font, the rest * are fall backs for characters not present in the main font. */ public List<FontInfo> getFonts() { return mFonts; } private void initFont() { mTypeface = Typeface.DEFAULT; updateFontObject(); } /** * Update the {@link Font} object from the typeface, text size and scaling */ @SuppressWarnings("deprecation") private void updateFontObject() { if (mTypeface != null) { // Get the fonts from the TypeFace object. List<Font> fonts = mTypeface.getFonts(); // create new font objects as well as FontMetrics, based on the current text size // and skew info. ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size()); for (Font font : fonts) { FontInfo info = new FontInfo(); info.mFont = font.deriveFont(mTextSize); if (mScaleX != 1.0 || mSkewX != 0) { // TODO: support skew info.mFont = info.mFont.deriveFont(new AffineTransform( mScaleX, mSkewX, 0, 0, 1, 0)); } info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); infoList.add(info); } mFonts = Collections.unmodifiableList(infoList); } } //---------------------------------------- public void set(Paint src) { if (this != src) { mColor = src.mColor; mTextSize = src.mTextSize; mScaleX = src.mScaleX; mSkewX = src.mSkewX; mAlign = src.mAlign; mStyle = src.mStyle; mFlags = src.mFlags; super.set(src); } } @Override public void setCompatibilityScaling(float factor) { super.setCompatibilityScaling(factor); } @Override public int getFlags() { return mFlags; } @Override public void setFlags(int flags) { mFlags = flags; } @Override public boolean isAntiAlias() { return super.isAntiAlias(); } @Override public boolean isDither() { return super.isDither(); } @Override public boolean isLinearText() { return super.isLinearText(); } @Override public boolean isStrikeThruText() { return super.isStrikeThruText(); } @Override public boolean isUnderlineText() { return super.isUnderlineText(); } @Override public boolean isFakeBoldText() { return super.isFakeBoldText(); } @Override public boolean isSubpixelText() { return super.isSubpixelText(); } @Override public boolean isFilterBitmap() { return super.isFilterBitmap(); } /** * Return the font's recommended interline spacing, given the Paint's * settings for typeface, textSize, etc. If metrics is not null, return the * fontmetric values in it. * * @param metrics If this object is not null, its fields are filled with * the appropriate values given the paint's text attributes. * @return the font's recommended interline spacing. */ public float getFontMetrics(FontMetrics metrics) { if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; if (metrics != null) { // Android expects negative ascent so we invert the value from Java. metrics.top = - javaMetrics.getMaxAscent(); metrics.ascent = - javaMetrics.getAscent(); metrics.descent = javaMetrics.getDescent(); metrics.bottom = javaMetrics.getMaxDescent(); metrics.leading = javaMetrics.getLeading(); } return javaMetrics.getHeight(); } return 0; } public int getFontMetricsInt(FontMetricsInt metrics) { if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; if (metrics != null) { // Android expects negative ascent so we invert the value from Java. metrics.top = - javaMetrics.getMaxAscent(); metrics.ascent = - javaMetrics.getAscent(); metrics.descent = javaMetrics.getDescent(); metrics.bottom = javaMetrics.getMaxDescent(); metrics.leading = javaMetrics.getLeading(); } return javaMetrics.getHeight(); } return 0; } /** * Reimplemented to return Paint.FontMetrics instead of _Original_Paint.FontMetrics */ public FontMetrics getFontMetrics() { FontMetrics fm = new FontMetrics(); getFontMetrics(fm); return fm; } /** * Reimplemented to return Paint.FontMetricsInt instead of _Original_Paint.FontMetricsInt */ public FontMetricsInt getFontMetricsInt() { FontMetricsInt fm = new FontMetricsInt(); getFontMetricsInt(fm); return fm; } @Override public float getFontMetrics(_Original_Paint.FontMetrics metrics) { throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } @Override public int getFontMetricsInt(_Original_Paint.FontMetricsInt metrics) { throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } @Override public Typeface setTypeface(Typeface typeface) { if (typeface != null) { mTypeface = typeface; } else { mTypeface = Typeface.DEFAULT; } updateFontObject(); return typeface; } @Override public Typeface getTypeface() { return super.getTypeface(); } @Override public int getColor() { return mColor; } @Override public void setColor(int color) { mColor = color; } @Override public void setARGB(int a, int r, int g, int b) { super.setARGB(a, r, g, b); } @Override public void setAlpha(int alpha) { mColor = (alpha << 24) | (mColor & 0x00FFFFFF); } @Override public int getAlpha() { return mColor >>> 24; } /** * Set or clear the shader object. * <p /> * Pass null to clear any previous shader. * As a convenience, the parameter passed is also returned. * * @param shader May be null. the new shader to be installed in the paint * @return shader */ @Override public Shader setShader(Shader shader) { return mShader = shader; } @Override public Shader getShader() { return super.getShader(); } /** * Set or clear the paint's colorfilter, returning the parameter. * * @param filter May be null. The new filter to be installed in the paint * @return filter */ @Override public ColorFilter setColorFilter(ColorFilter filter) { mColorFilter = filter; return filter; } @Override public ColorFilter getColorFilter() { return super.getColorFilter(); } /** * Set or clear the xfermode object. * <p /> * Pass null to clear any previous xfermode. * As a convenience, the parameter passed is also returned. * * @param xfermode May be null. The xfermode to be installed in the paint * @return xfermode */ @Override public Xfermode setXfermode(Xfermode xfermode) { return mXfermode = xfermode; } @Override public Xfermode getXfermode() { return super.getXfermode(); } @Override public Rasterizer setRasterizer(Rasterizer rasterizer) { mRasterizer = rasterizer; return rasterizer; } @Override public Rasterizer getRasterizer() { return super.getRasterizer(); } @Override public void setShadowLayer(float radius, float dx, float dy, int color) { // TODO Auto-generated method stub } @Override public void clearShadowLayer() { super.clearShadowLayer(); } public void setTextAlign(Align align) { mAlign = align; } @Override public void setTextAlign(android.graphics._Original_Paint.Align align) { throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } public Align getTextAlign() { return mAlign; } public void setStyle(Style style) { mStyle = style; } @Override public void setStyle(android.graphics._Original_Paint.Style style) { throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } public Style getStyle() { return mStyle; } @Override public void setDither(boolean dither) { mFlags |= dither ? DITHER_FLAG : ~DITHER_FLAG; } @Override public void setAntiAlias(boolean aa) { mFlags |= aa ? ANTI_ALIAS_FLAG : ~ANTI_ALIAS_FLAG; } @Override public void setFakeBoldText(boolean flag) { mFlags |= flag ? FAKE_BOLD_TEXT_FLAG : ~FAKE_BOLD_TEXT_FLAG; } @Override public void setLinearText(boolean flag) { mFlags |= flag ? LINEAR_TEXT_FLAG : ~LINEAR_TEXT_FLAG; } @Override public void setSubpixelText(boolean flag) { mFlags |= flag ? SUBPIXEL_TEXT_FLAG : ~SUBPIXEL_TEXT_FLAG; } @Override public void setUnderlineText(boolean flag) { mFlags |= flag ? UNDERLINE_TEXT_FLAG : ~UNDERLINE_TEXT_FLAG; } @Override public void setStrikeThruText(boolean flag) { mFlags |= flag ? STRIKE_THRU_TEXT_FLAG : ~STRIKE_THRU_TEXT_FLAG; } @Override public void setFilterBitmap(boolean flag) { mFlags |= flag ? FILTER_BITMAP_FLAG : ~FILTER_BITMAP_FLAG; } @Override public float getStrokeWidth() { return mStrokeWidth; } @Override public void setStrokeWidth(float width) { mStrokeWidth = width; } @Override public float getStrokeMiter() { return mStrokeMiter; } @Override public void setStrokeMiter(float miter) { mStrokeMiter = miter; } @Override public void setStrokeCap(android.graphics._Original_Paint.Cap cap) { throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } public void setStrokeCap(Cap cap) { mCap = cap; } public Cap getStrokeCap() { return mCap; } @Override public void setStrokeJoin(android.graphics._Original_Paint.Join join) { throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); } public void setStrokeJoin(Join join) { mJoin = join; } public Join getStrokeJoin() { return mJoin; } @Override public boolean getFillPath(Path src, Path dst) { return super.getFillPath(src, dst); } @Override public PathEffect setPathEffect(PathEffect effect) { mPathEffect = effect; return effect; } @Override public PathEffect getPathEffect() { return super.getPathEffect(); } @Override public MaskFilter setMaskFilter(MaskFilter maskfilter) { mMaskFilter = maskfilter; return maskfilter; } @Override public MaskFilter getMaskFilter() { return super.getMaskFilter(); } /** * Return the paint's text size. * * @return the paint's text size. */ @Override public float getTextSize() { return mTextSize; } /** * Set the paint's text size. This value must be > 0 * * @param textSize set the paint's text size. */ @Override public void setTextSize(float textSize) { mTextSize = textSize; updateFontObject(); } /** * Return the paint's horizontal scale factor for text. The default value * is 1.0. * * @return the paint's scale factor in X for drawing/measuring text */ @Override public float getTextScaleX() { return mScaleX; } /** * Set the paint's horizontal scale factor for text. The default value * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will * stretch the text narrower. * * @param scaleX set the paint's scale in X for drawing/measuring text. */ @Override public void setTextScaleX(float scaleX) { mScaleX = scaleX; updateFontObject(); } /** * Return the paint's horizontal skew factor for text. The default value * is 0. * * @return the paint's skew factor in X for drawing text. */ @Override public float getTextSkewX() { return mSkewX; } /** * Set the paint's horizontal skew factor for text. The default value * is 0. For approximating oblique text, use values around -0.25. * * @param skewX set the paint's skew factor in X for drawing text. */ @Override public void setTextSkewX(float skewX) { mSkewX = skewX; updateFontObject(); } @Override public float getFontSpacing() { return super.getFontSpacing(); } /** * Return the distance above (negative) the baseline (ascent) based on the * current typeface and text size. * * @return the distance above (negative) the baseline (ascent) based on the * current typeface and text size. */ @Override public float ascent() { if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; // Android expects negative ascent so we invert the value from Java. return - javaMetrics.getAscent(); } return 0; } /** * Return the distance below (positive) the baseline (descent) based on the * current typeface and text size. * * @return the distance below (positive) the baseline (descent) based on * the current typeface and text size. */ @Override public float descent() { if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; return javaMetrics.getDescent(); } return 0; } /** * Return the width of the text. * * @param text The text to measure * @param index The index of the first character to start measuring * @param count THe number of characters to measure, beginning with start * @return The width of the text */ @Override public float measureText(char[] text, int index, int count) { // WARNING: the logic in this method is similar to Canvas.drawText. // Any change to this method should be reflected in Canvas.drawText if (mFonts.size() > 0) { FontInfo mainFont = mFonts.get(0); int i = index; int lastIndex = index + count; float total = 0f; while (i < lastIndex) { // always start with the main font. int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); if (upTo == -1) { // shortcut to exit return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i); } else if (upTo > 0) { total += mainFont.mMetrics.charsWidth(text, i, upTo - i); i = upTo; // don't call continue at this point. Since it is certain the main font // cannot display the font a index upTo (now ==i), we move on to the // fallback fonts directly. } // no char supported, attempt to read the next char(s) with the // fallback font. In this case we only test the first character // and then go back to test with the main font. // Special test for 2-char characters. boolean foundFont = false; for (int f = 1 ; f < mFonts.size() ; f++) { FontInfo fontInfo = mFonts.get(f); // need to check that the font can display the character. We test // differently if the char is a high surrogate. int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); if (upTo == -1) { total += fontInfo.mMetrics.charsWidth(text, i, charCount); i += charCount; foundFont = true; break; } } // in case no font can display the char, measure it with the main font. if (foundFont == false) { int size = Character.isHighSurrogate(text[i]) ? 2 : 1; total += mainFont.mMetrics.charsWidth(text, i, size); i += size; } } } return 0; } /** * Return the width of the text. * * @param text The text to measure * @param start The index of the first character to start measuring * @param end 1 beyond the index of the last character to measure * @return The width of the text */ @Override public float measureText(String text, int start, int end) { return measureText(text.toCharArray(), start, end - start); } /** * Return the width of the text. * * @param text The text to measure * @return The width of the text */ @Override public float measureText(String text) { return measureText(text.toCharArray(), 0, text.length()); } /* * re-implement to call SpannableStringBuilder.measureText with a Paint object * instead of an _Original_Paint */ @Override public float measureText(CharSequence text, int start, int end) { if (text instanceof String) { return measureText((String)text, start, end); } if (text instanceof SpannedString || text instanceof SpannableString) { return measureText(text.toString(), start, end); } if (text instanceof SpannableStringBuilder) { return ((SpannableStringBuilder)text).measureText(start, end, this); } char[] buf = TemporaryBuffer.obtain(end - start); TextUtils.getChars(text, start, end, buf, 0); float result = measureText(buf, 0, end - start); TemporaryBuffer.recycle(buf); return result; } /** * Measure the text, stopping early if the measured width exceeds maxWidth. * Return the number of chars that were measured, and if measuredWidth is * not null, return in it the actual width measured. * * @param text The text to measure * @param index The offset into text to begin measuring at * @param count The number of maximum number of entries to measure. If count * is negative, then the characters before index are measured * in reverse order. This allows for measuring the end of * string. * @param maxWidth The maximum width to accumulate. * @param measuredWidth Optional. If not null, returns the actual width * measured. * @return The number of chars that were measured. Will always be <= * abs(count). */ @Override public int breakText(char[] text, int index, int count, float maxWidth, float[] measuredWidth) { int inc = count > 0 ? 1 : -1; int measureIndex = 0; float measureAcc = 0; for (int i = index ; i != index + count ; i += inc, measureIndex++) { int start, end; if (i < index) { start = i; end = index; } else { start = index; end = i; } // measure from start to end float res = measureText(text, start, end - start + 1); if (measuredWidth != null) { measuredWidth[measureIndex] = res; } measureAcc += res; if (res > maxWidth) { // we should not return this char index, but since it's 0-based and we need // to return a count, we simply return measureIndex; return measureIndex; } } return measureIndex; } /** * Measure the text, stopping early if the measured width exceeds maxWidth. * Return the number of chars that were measured, and if measuredWidth is * not null, return in it the actual width measured. * * @param text The text to measure * @param measureForwards If true, measure forwards, starting at index. * Otherwise, measure backwards, starting with the * last character in the string. * @param maxWidth The maximum width to accumulate. * @param measuredWidth Optional. If not null, returns the actual width * measured. * @return The number of chars that were measured. Will always be <= * abs(count). */ @Override public int breakText(String text, boolean measureForwards, float maxWidth, float[] measuredWidth) { return breakText(text, 0 /* start */, text.length() /* end */, measureForwards, maxWidth, measuredWidth); } /** * Measure the text, stopping early if the measured width exceeds maxWidth. * Return the number of chars that were measured, and if measuredWidth is * not null, return in it the actual width measured. * * @param text The text to measure * @param start The offset into text to begin measuring at * @param end The end of the text slice to measure. * @param measureForwards If true, measure forwards, starting at start. * Otherwise, measure backwards, starting with end. * @param maxWidth The maximum width to accumulate. * @param measuredWidth Optional. If not null, returns the actual width * measured. * @return The number of chars that were measured. Will always be <= * abs(end - start). */ @Override public int breakText(CharSequence text, int start, int end, boolean measureForwards, float maxWidth, float[] measuredWidth) { char[] buf = new char[end - start]; int result; TextUtils.getChars(text, start, end, buf, 0); if (measureForwards) { result = breakText(buf, 0, end - start, maxWidth, measuredWidth); } else { result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth); } return result; } /** * Return the advance widths for the characters in the string. * * @param text The text to measure * @param index The index of the first char to to measure * @param count The number of chars starting with index to measure * @param widths array to receive the advance widths of the characters. * Must be at least a large as count. * @return the actual number of widths returned. */ @Override public int getTextWidths(char[] text, int index, int count, float[] widths) { if (mFonts.size() > 0) { if ((index | count) < 0 || index + count > text.length || count > widths.length) { throw new ArrayIndexOutOfBoundsException(); } // FIXME: handle multi-char characters. // Need to figure out if the lengths of the width array takes into account // multi-char characters. for (int i = 0; i < count; i++) { char c = text[i + index]; boolean found = false; for (FontInfo info : mFonts) { if (info.mFont.canDisplay(c)) { widths[i] = info.mMetrics.charWidth(c); found = true; break; } } if (found == false) { // we stop there. return i; } } return count; } return 0; } /** * Return the advance widths for the characters in the string. * * @param text The text to measure * @param start The index of the first char to to measure * @param end The end of the text slice to measure * @param widths array to receive the advance widths of the characters. * Must be at least a large as the text. * @return the number of unichars in the specified text. */ @Override public int getTextWidths(String text, int start, int end, float[] widths) { if ((start | end | (end - start) | (text.length() - end)) < 0) { throw new IndexOutOfBoundsException(); } if (end - start > widths.length) { throw new ArrayIndexOutOfBoundsException(); } return getTextWidths(text.toCharArray(), start, end - start, widths); } /* * re-implement to call SpannableStringBuilder.getTextWidths with a Paint object * instead of an _Original_Paint */ @Override public int getTextWidths(CharSequence text, int start, int end, float[] widths) { if (text instanceof String) { return getTextWidths((String)text, start, end, widths); } if (text instanceof SpannedString || text instanceof SpannableString) { return getTextWidths(text.toString(), start, end, widths); } if (text instanceof SpannableStringBuilder) { return ((SpannableStringBuilder)text).getTextWidths(start, end, widths, this); } char[] buf = TemporaryBuffer.obtain(end - start); TextUtils.getChars(text, start, end, buf, 0); int result = getTextWidths(buf, 0, end - start, widths); TemporaryBuffer.recycle(buf); return result; } @Override public int getTextWidths(String text, float[] widths) { return super.getTextWidths(text, widths); } /** * Return the path (outline) for the specified text. * Note: just like Canvas.drawText, this will respect the Align setting in * the paint. * * @param text The text to retrieve the path from * @param index The index of the first character in text * @param count The number of characterss starting with index * @param x The x coordinate of the text's origin * @param y The y coordinate of the text's origin * @param path The path to receive the data describing the text. Must * be allocated by the caller. */ @Override public void getTextPath(char[] text, int index, int count, float x, float y, Path path) { // TODO this is the ORIGINAL implementation. REPLACE AS NEEDED OR REMOVE if ((index | count) < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } // TODO native_getTextPath(mNativePaint, text, index, count, x, y, path.ni()); throw new UnsupportedOperationException("IMPLEMENT AS NEEDED"); } /** * Return the path (outline) for the specified text. * Note: just like Canvas.drawText, this will respect the Align setting * in the paint. * * @param text The text to retrieve the path from * @param start The first character in the text * @param end 1 past the last charcter in the text * @param x The x coordinate of the text's origin * @param y The y coordinate of the text's origin * @param path The path to receive the data describing the text. Must * be allocated by the caller. */ @Override public void getTextPath(String text, int start, int end, float x, float y, Path path) { if ((start | end | (end - start) | (text.length() - end)) < 0) { throw new IndexOutOfBoundsException(); } getTextPath(text.toCharArray(), start, end - start, x, y, path); } /** * Return in bounds (allocated by the caller) the smallest rectangle that * encloses all of the characters, with an implied origin at (0,0). * * @param text String to measure and return its bounds * @param start Index of the first char in the string to measure * @param end 1 past the last char in the string measure * @param bounds Returns the unioned bounds of all the text. Must be * allocated by the caller. */ @Override public void getTextBounds(String text, int start, int end, Rect bounds) { if ((start | end | (end - start) | (text.length() - end)) < 0) { throw new IndexOutOfBoundsException(); } if (bounds == null) { throw new NullPointerException("need bounds Rect"); } getTextBounds(text.toCharArray(), start, end - start, bounds); } /** * Return in bounds (allocated by the caller) the smallest rectangle that * encloses all of the characters, with an implied origin at (0,0). * * @param text Array of chars to measure and return their unioned bounds * @param index Index of the first char in the array to measure * @param count The number of chars, beginning at index, to measure * @param bounds Returns the unioned bounds of all the text. Must be * allocated by the caller. */ @Override public void getTextBounds(char[] text, int index, int count, Rect bounds) { // FIXME if (mFonts.size() > 0) { if ((index | count) < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } if (bounds == null) { throw new NullPointerException("need bounds Rect"); } FontInfo mainInfo = mFonts.get(0); Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, mFontContext); bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight()); } } public static void finalizer(int foo) { // pass } }