OSDN Git Service

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