OSDN Git Service

merge from open-source master
[android-x86/frameworks-base.git] / tools / layoutlib / bridge / src / android / graphics / Paint.java
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package android.graphics;
18
19 import android.text.SpannableString;
20 import android.text.SpannableStringBuilder;
21 import android.text.SpannedString;
22 import android.text.TextUtils;
23
24 import java.awt.Font;
25 import java.awt.Toolkit;
26 import java.awt.font.FontRenderContext;
27 import java.awt.geom.AffineTransform;
28 import java.awt.geom.Rectangle2D;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.List;
32
33 /**
34  * A paint implementation overridden by the LayoutLib bridge.
35  */
36 public class Paint extends _Original_Paint {
37
38     private int mColor = 0xFFFFFFFF;
39     private float mStrokeWidth = 1.f;
40     private float mTextSize = 20;
41     private float mScaleX = 1;
42     private float mSkewX = 0;
43     private Align mAlign = Align.LEFT;
44     private Style mStyle = Style.FILL;
45     private float mStrokeMiter = 4.0f;
46     private Cap mCap = Cap.BUTT;
47     private Join mJoin = Join.MITER;
48     private int mFlags = 0;
49
50     /**
51      * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
52      */
53     public static final class FontInfo {
54         Font mFont;
55         java.awt.FontMetrics mMetrics;
56     }
57
58     private List<FontInfo> mFonts;
59     private final FontRenderContext mFontContext = new FontRenderContext(
60             new AffineTransform(), true, true);
61
62     @SuppressWarnings("hiding")
63     public static final int ANTI_ALIAS_FLAG       = _Original_Paint.ANTI_ALIAS_FLAG;
64     @SuppressWarnings("hiding")
65     public static final int FILTER_BITMAP_FLAG    = _Original_Paint.FILTER_BITMAP_FLAG;
66     @SuppressWarnings("hiding")
67     public static final int DITHER_FLAG           = _Original_Paint.DITHER_FLAG;
68     @SuppressWarnings("hiding")
69     public static final int UNDERLINE_TEXT_FLAG   = _Original_Paint.UNDERLINE_TEXT_FLAG;
70     @SuppressWarnings("hiding")
71     public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG;
72     @SuppressWarnings("hiding")
73     public static final int FAKE_BOLD_TEXT_FLAG   = _Original_Paint.FAKE_BOLD_TEXT_FLAG;
74     @SuppressWarnings("hiding")
75     public static final int LINEAR_TEXT_FLAG      = _Original_Paint.LINEAR_TEXT_FLAG;
76     @SuppressWarnings("hiding")
77     public static final int SUBPIXEL_TEXT_FLAG    = _Original_Paint.SUBPIXEL_TEXT_FLAG;
78     @SuppressWarnings("hiding")
79     public static final int DEV_KERN_TEXT_FLAG    = _Original_Paint.DEV_KERN_TEXT_FLAG;
80
81     public static class FontMetrics extends _Original_Paint.FontMetrics {
82     }
83
84     public static class FontMetricsInt extends _Original_Paint.FontMetricsInt {
85     }
86
87     /**
88      * The Style specifies if the primitive being drawn is filled,
89      * stroked, or both (in the same color). The default is FILL.
90      */
91     public enum Style {
92         /**
93          * Geometry and text drawn with this style will be filled, ignoring all
94          * stroke-related settings in the paint.
95          */
96         FILL            (0),
97         /**
98          * Geometry and text drawn with this style will be stroked, respecting
99          * the stroke-related fields on the paint.
100          */
101         STROKE          (1),
102         /**
103          * Geometry and text drawn with this style will be both filled and
104          * stroked at the same time, respecting the stroke-related fields on
105          * the paint.
106          */
107         FILL_AND_STROKE (2);
108
109         Style(int nativeInt) {
110             this.nativeInt = nativeInt;
111         }
112         final int nativeInt;
113     }
114
115     /**
116      * The Cap specifies the treatment for the beginning and ending of
117      * stroked lines and paths. The default is BUTT.
118      */
119     public enum Cap {
120         /**
121          * The stroke ends with the path, and does not project beyond it.
122          */
123         BUTT    (0),
124         /**
125          * The stroke projects out as a square, with the center at the end
126          * of the path.
127          */
128         ROUND   (1),
129         /**
130          * The stroke projects out as a semicircle, with the center at the
131          * end of the path.
132          */
133         SQUARE  (2);
134
135         private Cap(int nativeInt) {
136             this.nativeInt = nativeInt;
137         }
138         final int nativeInt;
139     }
140
141     /**
142      * The Join specifies the treatment where lines and curve segments
143      * join on a stroked path. The default is MITER.
144      */
145     public enum Join {
146         /**
147          * The outer edges of a join meet at a sharp angle
148          */
149         MITER   (0),
150         /**
151          * The outer edges of a join meet in a circular arc.
152          */
153         ROUND   (1),
154         /**
155          * The outer edges of a join meet with a straight line
156          */
157         BEVEL   (2);
158
159         private Join(int nativeInt) {
160             this.nativeInt = nativeInt;
161         }
162         final int nativeInt;
163     }
164
165     /**
166      * Align specifies how drawText aligns its text relative to the
167      * [x,y] coordinates. The default is LEFT.
168      */
169     public enum Align {
170         /**
171          * The text is drawn to the right of the x,y origin
172          */
173         LEFT    (0),
174         /**
175          * The text is drawn centered horizontally on the x,y origin
176          */
177         CENTER  (1),
178         /**
179          * The text is drawn to the left of the x,y origin
180          */
181         RIGHT   (2);
182
183         private Align(int nativeInt) {
184             this.nativeInt = nativeInt;
185         }
186         final int nativeInt;
187     }
188
189     public Paint() {
190         this(0);
191     }
192
193     public Paint(int flags) {
194         setFlags(flags | DEFAULT_PAINT_FLAGS);
195         initFont();
196     }
197
198     public Paint(Paint paint) {
199         set(paint);
200         initFont();
201     }
202
203     @Override
204     public void finalize() throws Throwable {
205         // pass
206     }
207
208     @Override
209     public void reset() {
210         super.reset();
211     }
212
213     /**
214      * Returns the list of {@link Font} objects. The first item is the main font, the rest
215      * are fall backs for characters not present in the main font.
216      */
217     public List<FontInfo> getFonts() {
218         return mFonts;
219     }
220
221     private void initFont() {
222         mTypeface = Typeface.DEFAULT;
223         updateFontObject();
224     }
225
226     /**
227      * Update the {@link Font} object from the typeface, text size and scaling
228      */
229     @SuppressWarnings("deprecation")
230     private void updateFontObject() {
231         if (mTypeface != null) {
232             // Get the fonts from the TypeFace object.
233             List<Font> fonts = mTypeface.getFonts();
234
235             // create new font objects as well as FontMetrics, based on the current text size
236             // and skew info.
237             ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
238             for (Font font : fonts) {
239                 FontInfo info = new FontInfo();
240                 info.mFont = font.deriveFont(mTextSize);
241                 if (mScaleX != 1.0 || mSkewX != 0) {
242                     // TODO: support skew
243                     info.mFont = info.mFont.deriveFont(new AffineTransform(
244                             mScaleX, mSkewX, 0, 0, 1, 0));
245                 }
246                 info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
247
248                 infoList.add(info);
249             }
250
251             mFonts = Collections.unmodifiableList(infoList);
252         }
253     }
254
255     //----------------------------------------
256
257     public void set(Paint src) {
258         if (this != src) {
259             mColor = src.mColor;
260             mTextSize = src.mTextSize;
261             mScaleX = src.mScaleX;
262             mSkewX = src.mSkewX;
263             mAlign = src.mAlign;
264             mStyle = src.mStyle;
265             mFlags = src.mFlags;
266
267             super.set(src);
268         }
269     }
270
271     @Override
272     public void setCompatibilityScaling(float factor) {
273         super.setCompatibilityScaling(factor);
274     }
275
276     @Override
277     public int getFlags() {
278         return mFlags;
279     }
280
281     @Override
282     public void setFlags(int flags) {
283         mFlags = flags;
284     }
285
286     @Override
287     public boolean isAntiAlias() {
288         return super.isAntiAlias();
289     }
290
291     @Override
292     public boolean isDither() {
293         return super.isDither();
294     }
295
296     @Override
297     public boolean isLinearText() {
298         return super.isLinearText();
299     }
300
301     @Override
302     public boolean isStrikeThruText() {
303         return super.isStrikeThruText();
304     }
305
306     @Override
307     public boolean isUnderlineText() {
308         return super.isUnderlineText();
309     }
310
311     @Override
312     public boolean isFakeBoldText() {
313         return super.isFakeBoldText();
314     }
315
316     @Override
317     public boolean isSubpixelText() {
318         return super.isSubpixelText();
319     }
320
321     @Override
322     public boolean isFilterBitmap() {
323         return super.isFilterBitmap();
324     }
325
326     /**
327      * Return the font's recommended interline spacing, given the Paint's
328      * settings for typeface, textSize, etc. If metrics is not null, return the
329      * fontmetric values in it.
330      *
331      * @param metrics If this object is not null, its fields are filled with
332      *                the appropriate values given the paint's text attributes.
333      * @return the font's recommended interline spacing.
334      */
335     public float getFontMetrics(FontMetrics metrics) {
336         if (mFonts.size() > 0) {
337             java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
338             if (metrics != null) {
339                 // Android expects negative ascent so we invert the value from Java.
340                 metrics.top = - javaMetrics.getMaxAscent();
341                 metrics.ascent = - javaMetrics.getAscent();
342                 metrics.descent = javaMetrics.getDescent();
343                 metrics.bottom = javaMetrics.getMaxDescent();
344                 metrics.leading = javaMetrics.getLeading();
345             }
346
347             return javaMetrics.getHeight();
348         }
349
350         return 0;
351     }
352
353     public int getFontMetricsInt(FontMetricsInt metrics) {
354         if (mFonts.size() > 0) {
355             java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
356             if (metrics != null) {
357                 // Android expects negative ascent so we invert the value from Java.
358                 metrics.top = - javaMetrics.getMaxAscent();
359                 metrics.ascent = - javaMetrics.getAscent();
360                 metrics.descent = javaMetrics.getDescent();
361                 metrics.bottom = javaMetrics.getMaxDescent();
362                 metrics.leading = javaMetrics.getLeading();
363             }
364
365             return javaMetrics.getHeight();
366         }
367
368         return 0;
369     }
370
371     /**
372      * Reimplemented to return Paint.FontMetrics instead of _Original_Paint.FontMetrics
373      */
374     public FontMetrics getFontMetrics() {
375         FontMetrics fm = new FontMetrics();
376         getFontMetrics(fm);
377         return fm;
378     }
379
380     /**
381      * Reimplemented to return Paint.FontMetricsInt instead of _Original_Paint.FontMetricsInt
382      */
383     public FontMetricsInt getFontMetricsInt() {
384         FontMetricsInt fm = new FontMetricsInt();
385         getFontMetricsInt(fm);
386         return fm;
387     }
388
389
390
391     @Override
392     public float getFontMetrics(_Original_Paint.FontMetrics metrics) {
393         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
394     }
395
396     @Override
397     public int getFontMetricsInt(_Original_Paint.FontMetricsInt metrics) {
398         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
399     }
400
401     @Override
402     public Typeface setTypeface(Typeface typeface) {
403         if (typeface != null) {
404             mTypeface = typeface;
405         } else {
406             mTypeface = Typeface.DEFAULT;
407         }
408
409         updateFontObject();
410
411         return typeface;
412     }
413
414     @Override
415     public Typeface getTypeface() {
416         return super.getTypeface();
417     }
418
419     @Override
420     public int getColor() {
421         return mColor;
422     }
423
424     @Override
425     public void setColor(int color) {
426         mColor = color;
427     }
428
429     @Override
430     public void setARGB(int a, int r, int g, int b) {
431         super.setARGB(a, r, g, b);
432     }
433
434     @Override
435     public void setAlpha(int alpha) {
436         mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
437     }
438
439     @Override
440     public int getAlpha() {
441         return mColor >>> 24;
442     }
443
444     /**
445      * Set or clear the shader object.
446      * <p />
447      * Pass null to clear any previous shader.
448      * As a convenience, the parameter passed is also returned.
449      *
450      * @param shader May be null. the new shader to be installed in the paint
451      * @return       shader
452      */
453     @Override
454     public Shader setShader(Shader shader) {
455         return mShader = shader;
456     }
457
458     @Override
459     public Shader getShader() {
460         return super.getShader();
461     }
462
463     /**
464      * Set or clear the paint's colorfilter, returning the parameter.
465      *
466      * @param filter May be null. The new filter to be installed in the paint
467      * @return       filter
468      */
469     @Override
470     public ColorFilter setColorFilter(ColorFilter filter) {
471         mColorFilter = filter;
472         return filter;
473     }
474
475     @Override
476     public ColorFilter getColorFilter() {
477         return super.getColorFilter();
478     }
479
480     /**
481      * Set or clear the xfermode object.
482      * <p />
483      * Pass null to clear any previous xfermode.
484      * As a convenience, the parameter passed is also returned.
485      *
486      * @param xfermode May be null. The xfermode to be installed in the paint
487      * @return         xfermode
488      */
489     @Override
490     public Xfermode setXfermode(Xfermode xfermode) {
491         return mXfermode = xfermode;
492     }
493
494     @Override
495     public Xfermode getXfermode() {
496         return super.getXfermode();
497     }
498
499     @Override
500     public Rasterizer setRasterizer(Rasterizer rasterizer) {
501         mRasterizer = rasterizer;
502         return rasterizer;
503     }
504
505     @Override
506     public Rasterizer getRasterizer() {
507         return super.getRasterizer();
508     }
509
510     @Override
511     public void setShadowLayer(float radius, float dx, float dy, int color) {
512         // TODO Auto-generated method stub
513     }
514
515     @Override
516     public void clearShadowLayer() {
517         super.clearShadowLayer();
518     }
519
520     public void setTextAlign(Align align) {
521         mAlign = align;
522     }
523
524     @Override
525     public void setTextAlign(android.graphics._Original_Paint.Align align) {
526         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
527     }
528
529     public Align getTextAlign() {
530         return mAlign;
531     }
532
533     public void setStyle(Style style) {
534         mStyle = style;
535     }
536
537     @Override
538     public void setStyle(android.graphics._Original_Paint.Style style) {
539         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
540     }
541
542     public Style getStyle() {
543         return mStyle;
544     }
545
546     @Override
547     public void setDither(boolean dither) {
548         mFlags |= dither ? DITHER_FLAG : ~DITHER_FLAG;
549     }
550
551     @Override
552     public void setAntiAlias(boolean aa) {
553         mFlags |= aa ? ANTI_ALIAS_FLAG : ~ANTI_ALIAS_FLAG;
554     }
555
556     @Override
557     public void setFakeBoldText(boolean flag) {
558         mFlags |= flag ? FAKE_BOLD_TEXT_FLAG : ~FAKE_BOLD_TEXT_FLAG;
559     }
560
561     @Override
562     public void setLinearText(boolean flag) {
563         mFlags |= flag ? LINEAR_TEXT_FLAG : ~LINEAR_TEXT_FLAG;
564     }
565
566     @Override
567     public void setSubpixelText(boolean flag) {
568         mFlags |= flag ? SUBPIXEL_TEXT_FLAG : ~SUBPIXEL_TEXT_FLAG;
569     }
570
571     @Override
572     public void setUnderlineText(boolean flag) {
573         mFlags |= flag ? UNDERLINE_TEXT_FLAG : ~UNDERLINE_TEXT_FLAG;
574     }
575
576     @Override
577     public void setStrikeThruText(boolean flag) {
578         mFlags |= flag ? STRIKE_THRU_TEXT_FLAG : ~STRIKE_THRU_TEXT_FLAG;
579     }
580
581     @Override
582     public void setFilterBitmap(boolean flag) {
583         mFlags |= flag ? FILTER_BITMAP_FLAG : ~FILTER_BITMAP_FLAG;
584     }
585
586     @Override
587     public float getStrokeWidth() {
588         return mStrokeWidth;
589     }
590
591     @Override
592     public void setStrokeWidth(float width) {
593         mStrokeWidth = width;
594     }
595
596     @Override
597     public float getStrokeMiter() {
598         return mStrokeMiter;
599     }
600
601     @Override
602     public void setStrokeMiter(float miter) {
603         mStrokeMiter = miter;
604     }
605
606     @Override
607     public void setStrokeCap(android.graphics._Original_Paint.Cap cap) {
608         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
609     }
610
611     public void setStrokeCap(Cap cap) {
612         mCap = cap;
613     }
614
615     public Cap getStrokeCap() {
616         return mCap;
617     }
618
619     @Override
620     public void setStrokeJoin(android.graphics._Original_Paint.Join join) {
621         throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
622     }
623
624     public void setStrokeJoin(Join join) {
625         mJoin = join;
626     }
627
628     public Join getStrokeJoin() {
629         return mJoin;
630     }
631
632     @Override
633     public boolean getFillPath(Path src, Path dst) {
634         return super.getFillPath(src, dst);
635     }
636
637     @Override
638     public PathEffect setPathEffect(PathEffect effect) {
639         mPathEffect = effect;
640         return effect;
641     }
642
643     @Override
644     public PathEffect getPathEffect() {
645         return super.getPathEffect();
646     }
647
648     @Override
649     public MaskFilter setMaskFilter(MaskFilter maskfilter) {
650         mMaskFilter = maskfilter;
651         return maskfilter;
652     }
653
654     @Override
655     public MaskFilter getMaskFilter() {
656         return super.getMaskFilter();
657     }
658
659     /**
660      * Return the paint's text size.
661      *
662      * @return the paint's text size.
663      */
664     @Override
665     public float getTextSize() {
666         return mTextSize;
667     }
668
669     /**
670      * Set the paint's text size. This value must be > 0
671      *
672      * @param textSize set the paint's text size.
673      */
674     @Override
675     public void setTextSize(float textSize) {
676         mTextSize = textSize;
677
678         updateFontObject();
679     }
680
681     /**
682      * Return the paint's horizontal scale factor for text. The default value
683      * is 1.0.
684      *
685      * @return the paint's scale factor in X for drawing/measuring text
686      */
687     @Override
688     public float getTextScaleX() {
689         return mScaleX;
690     }
691
692     /**
693      * Set the paint's horizontal scale factor for text. The default value
694      * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
695      * stretch the text narrower.
696      *
697      * @param scaleX set the paint's scale in X for drawing/measuring text.
698      */
699     @Override
700     public void setTextScaleX(float scaleX) {
701         mScaleX = scaleX;
702
703         updateFontObject();
704     }
705
706     /**
707      * Return the paint's horizontal skew factor for text. The default value
708      * is 0.
709      *
710      * @return         the paint's skew factor in X for drawing text.
711      */
712     @Override
713     public float getTextSkewX() {
714         return mSkewX;
715     }
716
717     /**
718      * Set the paint's horizontal skew factor for text. The default value
719      * is 0. For approximating oblique text, use values around -0.25.
720      *
721      * @param skewX set the paint's skew factor in X for drawing text.
722      */
723     @Override
724     public void setTextSkewX(float skewX) {
725         mSkewX = skewX;
726
727         updateFontObject();
728     }
729
730     @Override
731     public float getFontSpacing() {
732         return super.getFontSpacing();
733     }
734
735     /**
736      * Return the distance above (negative) the baseline (ascent) based on the
737      * current typeface and text size.
738      *
739      * @return the distance above (negative) the baseline (ascent) based on the
740      *         current typeface and text size.
741      */
742     @Override
743     public float ascent() {
744         if (mFonts.size() > 0) {
745             java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
746             // Android expects negative ascent so we invert the value from Java.
747             return - javaMetrics.getAscent();
748         }
749
750         return 0;
751     }
752
753     /**
754      * Return the distance below (positive) the baseline (descent) based on the
755      * current typeface and text size.
756      *
757      * @return the distance below (positive) the baseline (descent) based on
758      *         the current typeface and text size.
759      */
760     @Override
761     public float descent() {
762         if (mFonts.size() > 0) {
763             java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
764             return javaMetrics.getDescent();
765         }
766
767         return 0;
768     }
769
770     /**
771      * Return the width of the text.
772      *
773      * @param text  The text to measure
774      * @param index The index of the first character to start measuring
775      * @param count THe number of characters to measure, beginning with start
776      * @return      The width of the text
777      */
778     @Override
779     public float measureText(char[] text, int index, int count) {
780         // WARNING: the logic in this method is similar to Canvas.drawText.
781         // Any change to this method should be reflected in Canvas.drawText
782         if (mFonts.size() > 0) {
783             FontInfo mainFont = mFonts.get(0);
784             int i = index;
785             int lastIndex = index + count;
786             float total = 0f;
787             while (i < lastIndex) {
788                 // always start with the main font.
789                 int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
790                 if (upTo == -1) {
791                     // shortcut to exit
792                     return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
793                 } else if (upTo > 0) {
794                     total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
795                     i = upTo;
796                     // don't call continue at this point. Since it is certain the main font
797                     // cannot display the font a index upTo (now ==i), we move on to the
798                     // fallback fonts directly.
799                 }
800
801                 // no char supported, attempt to read the next char(s) with the
802                 // fallback font. In this case we only test the first character
803                 // and then go back to test with the main font.
804                 // Special test for 2-char characters.
805                 boolean foundFont = false;
806                 for (int f = 1 ; f < mFonts.size() ; f++) {
807                     FontInfo fontInfo = mFonts.get(f);
808
809                     // need to check that the font can display the character. We test
810                     // differently if the char is a high surrogate.
811                     int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
812                     upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
813                     if (upTo == -1) {
814                         total += fontInfo.mMetrics.charsWidth(text, i, charCount);
815                         i += charCount;
816                         foundFont = true;
817                         break;
818
819                     }
820                 }
821
822                 // in case no font can display the char, measure it with the main font.
823                 if (foundFont == false) {
824                     int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
825                     total += mainFont.mMetrics.charsWidth(text, i, size);
826                     i += size;
827                 }
828             }
829         }
830
831         return 0;
832     }
833
834     /**
835      * Return the width of the text.
836      *
837      * @param text  The text to measure
838      * @param start The index of the first character to start measuring
839      * @param end   1 beyond the index of the last character to measure
840      * @return      The width of the text
841      */
842     @Override
843     public float measureText(String text, int start, int end) {
844         return measureText(text.toCharArray(), start, end - start);
845     }
846
847     /**
848      * Return the width of the text.
849      *
850      * @param text  The text to measure
851      * @return      The width of the text
852      */
853     @Override
854     public float measureText(String text) {
855         return measureText(text.toCharArray(), 0, text.length());
856     }
857
858     /*
859      * re-implement to call SpannableStringBuilder.measureText with a Paint object
860      * instead of an _Original_Paint
861      */
862     @Override
863     public float measureText(CharSequence text, int start, int end) {
864         if (text instanceof String) {
865             return measureText((String)text, start, end);
866         }
867         if (text instanceof SpannedString ||
868             text instanceof SpannableString) {
869             return measureText(text.toString(), start, end);
870         }
871         if (text instanceof SpannableStringBuilder) {
872             return ((SpannableStringBuilder)text).measureText(start, end, this);
873         }
874
875         char[] buf = TemporaryBuffer.obtain(end - start);
876         TextUtils.getChars(text, start, end, buf, 0);
877         float result = measureText(buf, 0, end - start);
878         TemporaryBuffer.recycle(buf);
879         return result;
880     }
881
882     /**
883      * Measure the text, stopping early if the measured width exceeds maxWidth.
884      * Return the number of chars that were measured, and if measuredWidth is
885      * not null, return in it the actual width measured.
886      *
887      * @param text  The text to measure
888      * @param index The offset into text to begin measuring at
889      * @param count The number of maximum number of entries to measure. If count
890      *              is negative, then the characters before index are measured
891      *              in reverse order. This allows for measuring the end of
892      *              string.
893      * @param maxWidth The maximum width to accumulate.
894      * @param measuredWidth Optional. If not null, returns the actual width
895      *                     measured.
896      * @return The number of chars that were measured. Will always be <=
897      *         abs(count).
898      */
899     @Override
900     public int breakText(char[] text, int index, int count,
901                                 float maxWidth, float[] measuredWidth) {
902         int inc = count > 0 ? 1 : -1;
903
904         int measureIndex = 0;
905         float measureAcc = 0;
906         for (int i = index ; i != index + count ; i += inc, measureIndex++) {
907             int start, end;
908             if (i < index) {
909                 start = i;
910                 end = index;
911             } else {
912                 start = index;
913                 end = i;
914             }
915
916             // measure from start to end
917             float res = measureText(text, start, end - start + 1);
918
919             if (measuredWidth != null) {
920                 measuredWidth[measureIndex] = res;
921             }
922
923             measureAcc += res;
924             if (res > maxWidth) {
925                 // we should not return this char index, but since it's 0-based and we need
926                 // to return a count, we simply return measureIndex;
927                 return measureIndex;
928             }
929
930         }
931
932         return measureIndex;
933     }
934
935     /**
936      * Measure the text, stopping early if the measured width exceeds maxWidth.
937      * Return the number of chars that were measured, and if measuredWidth is
938      * not null, return in it the actual width measured.
939      *
940      * @param text  The text to measure
941      * @param measureForwards If true, measure forwards, starting at index.
942      *                        Otherwise, measure backwards, starting with the
943      *                        last character in the string.
944      * @param maxWidth The maximum width to accumulate.
945      * @param measuredWidth Optional. If not null, returns the actual width
946      *                     measured.
947      * @return The number of chars that were measured. Will always be <=
948      *         abs(count).
949      */
950     @Override
951     public int breakText(String text, boolean measureForwards,
952                                 float maxWidth, float[] measuredWidth) {
953         return breakText(text,
954                 0 /* start */, text.length() /* end */,
955                 measureForwards, maxWidth, measuredWidth);
956     }
957
958     /**
959      * Measure the text, stopping early if the measured width exceeds maxWidth.
960      * Return the number of chars that were measured, and if measuredWidth is
961      * not null, return in it the actual width measured.
962      *
963      * @param text  The text to measure
964      * @param start The offset into text to begin measuring at
965      * @param end   The end of the text slice to measure.
966      * @param measureForwards If true, measure forwards, starting at start.
967      *                        Otherwise, measure backwards, starting with end.
968      * @param maxWidth The maximum width to accumulate.
969      * @param measuredWidth Optional. If not null, returns the actual width
970      *                     measured.
971      * @return The number of chars that were measured. Will always be <=
972      *         abs(end - start).
973      */
974     @Override
975     public int breakText(CharSequence text, int start, int end, boolean measureForwards,
976             float maxWidth, float[] measuredWidth) {
977         char[] buf = new char[end - start];
978         int result;
979
980         TextUtils.getChars(text, start, end, buf, 0);
981
982         if (measureForwards) {
983             result = breakText(buf, 0, end - start, maxWidth, measuredWidth);
984         } else {
985             result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth);
986         }
987
988         return result;
989     }
990
991     /**
992      * Return the advance widths for the characters in the string.
993      *
994      * @param text     The text to measure
995      * @param index    The index of the first char to to measure
996      * @param count    The number of chars starting with index to measure
997      * @param widths   array to receive the advance widths of the characters.
998      *                 Must be at least a large as count.
999      * @return         the actual number of widths returned.
1000      */
1001     @Override
1002     public int getTextWidths(char[] text, int index, int count,
1003                              float[] widths) {
1004         if (mFonts.size() > 0) {
1005             if ((index | count) < 0 || index + count > text.length
1006                     || count > widths.length) {
1007                 throw new ArrayIndexOutOfBoundsException();
1008             }
1009
1010             // FIXME: handle multi-char characters.
1011             // Need to figure out if the lengths of the width array takes into account
1012             // multi-char characters.
1013             for (int i = 0; i < count; i++) {
1014                 char c = text[i + index];
1015                 boolean found = false;
1016                 for (FontInfo info : mFonts) {
1017                     if (info.mFont.canDisplay(c)) {
1018                         widths[i] = info.mMetrics.charWidth(c);
1019                         found = true;
1020                         break;
1021                     }
1022                 }
1023
1024                 if (found == false) {
1025                     // we stop there.
1026                     return i;
1027                 }
1028             }
1029
1030             return count;
1031         }
1032
1033         return 0;
1034     }
1035
1036     /**
1037      * Return the advance widths for the characters in the string.
1038      *
1039      * @param text   The text to measure
1040      * @param start  The index of the first char to to measure
1041      * @param end    The end of the text slice to measure
1042      * @param widths array to receive the advance widths of the characters.
1043      *               Must be at least a large as the text.
1044      * @return       the number of unichars in the specified text.
1045      */
1046     @Override
1047     public int getTextWidths(String text, int start, int end, float[] widths) {
1048         if ((start | end | (end - start) | (text.length() - end)) < 0) {
1049             throw new IndexOutOfBoundsException();
1050         }
1051         if (end - start > widths.length) {
1052             throw new ArrayIndexOutOfBoundsException();
1053         }
1054
1055         return getTextWidths(text.toCharArray(), start, end - start, widths);
1056     }
1057
1058     /*
1059      * re-implement to call SpannableStringBuilder.getTextWidths with a Paint object
1060      * instead of an _Original_Paint
1061      */
1062     @Override
1063     public int getTextWidths(CharSequence text, int start, int end, float[] widths) {
1064         if (text instanceof String) {
1065             return getTextWidths((String)text, start, end, widths);
1066         }
1067         if (text instanceof SpannedString || text instanceof SpannableString) {
1068             return getTextWidths(text.toString(), start, end, widths);
1069         }
1070         if (text instanceof SpannableStringBuilder) {
1071             return ((SpannableStringBuilder)text).getTextWidths(start, end, widths, this);
1072         }
1073
1074         char[] buf = TemporaryBuffer.obtain(end - start);
1075         TextUtils.getChars(text, start, end, buf, 0);
1076         int result = getTextWidths(buf, 0, end - start, widths);
1077         TemporaryBuffer.recycle(buf);
1078         return result;
1079     }
1080
1081     @Override
1082     public int getTextWidths(String text, float[] widths) {
1083         return super.getTextWidths(text, widths);
1084     }
1085
1086     /**
1087      * Return the path (outline) for the specified text.
1088      * Note: just like Canvas.drawText, this will respect the Align setting in
1089      * the paint.
1090      *
1091      * @param text     The text to retrieve the path from
1092      * @param index    The index of the first character in text
1093      * @param count    The number of characterss starting with index
1094      * @param x        The x coordinate of the text's origin
1095      * @param y        The y coordinate of the text's origin
1096      * @param path     The path to receive the data describing the text. Must
1097      *                 be allocated by the caller.
1098      */
1099     @Override
1100     public void getTextPath(char[] text, int index, int count,
1101                             float x, float y, Path path) {
1102
1103         // TODO this is the ORIGINAL implementation. REPLACE AS NEEDED OR REMOVE
1104
1105         if ((index | count) < 0 || index + count > text.length) {
1106             throw new ArrayIndexOutOfBoundsException();
1107         }
1108
1109         // TODO native_getTextPath(mNativePaint, text, index, count, x, y, path.ni());
1110
1111         throw new UnsupportedOperationException("IMPLEMENT AS NEEDED");
1112     }
1113
1114     /**
1115      * Return the path (outline) for the specified text.
1116      * Note: just like Canvas.drawText, this will respect the Align setting
1117      * in the paint.
1118      *
1119      * @param text  The text to retrieve the path from
1120      * @param start The first character in the text
1121      * @param end   1 past the last charcter in the text
1122      * @param x     The x coordinate of the text's origin
1123      * @param y     The y coordinate of the text's origin
1124      * @param path  The path to receive the data describing the text. Must
1125      *              be allocated by the caller.
1126      */
1127     @Override
1128     public void getTextPath(String text, int start, int end,
1129                             float x, float y, Path path) {
1130         if ((start | end | (end - start) | (text.length() - end)) < 0) {
1131             throw new IndexOutOfBoundsException();
1132         }
1133
1134         getTextPath(text.toCharArray(), start, end - start, x, y, path);
1135     }
1136
1137     /**
1138      * Return in bounds (allocated by the caller) the smallest rectangle that
1139      * encloses all of the characters, with an implied origin at (0,0).
1140      *
1141      * @param text  String to measure and return its bounds
1142      * @param start Index of the first char in the string to measure
1143      * @param end   1 past the last char in the string measure
1144      * @param bounds Returns the unioned bounds of all the text. Must be
1145      *               allocated by the caller.
1146      */
1147     @Override
1148     public void getTextBounds(String text, int start, int end, Rect bounds) {
1149         if ((start | end | (end - start) | (text.length() - end)) < 0) {
1150             throw new IndexOutOfBoundsException();
1151         }
1152         if (bounds == null) {
1153             throw new NullPointerException("need bounds Rect");
1154         }
1155
1156         getTextBounds(text.toCharArray(), start, end - start, bounds);
1157     }
1158
1159     /**
1160      * Return in bounds (allocated by the caller) the smallest rectangle that
1161      * encloses all of the characters, with an implied origin at (0,0).
1162      *
1163      * @param text  Array of chars to measure and return their unioned bounds
1164      * @param index Index of the first char in the array to measure
1165      * @param count The number of chars, beginning at index, to measure
1166      * @param bounds Returns the unioned bounds of all the text. Must be
1167      *               allocated by the caller.
1168      */
1169     @Override
1170     public void getTextBounds(char[] text, int index, int count, Rect bounds) {
1171         // FIXME
1172         if (mFonts.size() > 0) {
1173             if ((index | count) < 0 || index + count > text.length) {
1174                 throw new ArrayIndexOutOfBoundsException();
1175             }
1176             if (bounds == null) {
1177                 throw new NullPointerException("need bounds Rect");
1178             }
1179
1180             FontInfo mainInfo = mFonts.get(0);
1181
1182             Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, mFontContext);
1183             bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight());
1184         }
1185     }
1186
1187     public static void finalizer(int foo) {
1188         // pass
1189     }
1190 }