1/*
2 * Copyright (C) 2010 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
17package android.graphics;
18
19import com.android.annotations.NonNull;
20import com.android.layoutlib.bridge.impl.DelegateManager;
21import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
22
23import android.graphics.FontFamily_Delegate.FontVariant;
24
25import java.awt.Font;
26import java.io.File;
27import java.util.ArrayList;
28import java.util.List;
29
30import static android.graphics.FontFamily_Delegate.getFontLocation;
31
32/**
33 * Delegate implementing the native methods of android.graphics.Typeface
34 *
35 * Through the layoutlib_create tool, the original native methods of Typeface have been replaced
36 * by calls to methods of the same name in this delegate class.
37 *
38 * This class behaves like the original native implementation, but in Java, keeping previously
39 * native data into its own objects and mapping them to int that are sent back and forth between
40 * it and the original Typeface class.
41 *
42 * @see DelegateManager
43 *
44 */
45public final class Typeface_Delegate {
46
47    public static final String SYSTEM_FONTS = "/system/fonts/";
48
49    // ---- delegate manager ----
50    private static final DelegateManager<Typeface_Delegate> sManager =
51            new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
52
53
54    // ---- delegate data ----
55
56    @NonNull
57    private final FontFamily_Delegate[] mFontFamilies;  // the reference to FontFamily_Delegate.
58    /** @see Font#getStyle() */
59    private final int mStyle;
60    private final int mWeight;
61
62    private static long sDefaultTypeface;
63
64
65    // ---- Public Helper methods ----
66
67    public static Typeface_Delegate getDelegate(long nativeTypeface) {
68        return sManager.getDelegate(nativeTypeface);
69    }
70
71    /**
72     * Return a list of fonts that match the style and variant. The list is ordered according to
73     * preference of fonts.
74     *
75     * The list may contain null when the font failed to load. If null is reached when trying to
76     * render with this list of fonts, then a warning should be logged letting the user know that
77     * some font failed to load.
78     *
79     * @param variant The variant preferred. Can only be {@link FontVariant#COMPACT} or
80     *                {@link FontVariant#ELEGANT}
81     */
82    @NonNull
83    public List<Font> getFonts(FontVariant variant) {
84        assert variant != FontVariant.NONE;
85
86        // Calculate the required weight based on style and weight of this typeface.
87        int weight = mWeight + ((mStyle & Font.BOLD) == 0 ? 0 : FontFamily_Delegate.BOLD_FONT_WEIGHT_DELTA);
88        if (weight > 900) {
89            weight = 900;
90        }
91        final boolean isItalic = (mStyle & Font.ITALIC) != 0;
92        List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
93        for (int i = 0; i < mFontFamilies.length; i++) {
94            FontFamily_Delegate ffd = mFontFamilies[i];
95            if (ffd != null && ffd.isValid()) {
96                Font font = ffd.getFont(weight, isItalic);
97                if (font != null) {
98                    FontVariant ffdVariant = ffd.getVariant();
99                    if (ffdVariant == FontVariant.NONE) {
100                        fonts.add(font);
101                        continue;
102                    }
103                    // We cannot open each font and get locales supported, etc to match the fonts.
104                    // As a workaround, we hardcode certain assumptions like Elegant and Compact
105                    // always appear in pairs.
106                    assert i < mFontFamilies.length - 1;
107                    FontFamily_Delegate ffd2 = mFontFamilies[++i];
108                    assert ffd2 != null;
109                    FontVariant ffd2Variant = ffd2.getVariant();
110                    Font font2 = ffd2.getFont(weight, isItalic);
111                    assert ffd2Variant != FontVariant.NONE && ffd2Variant != ffdVariant
112                            && font2 != null;
113                    // Add the font with the matching variant to the list.
114                    if (variant == ffd.getVariant()) {
115                        fonts.add(font);
116                    } else {
117                        fonts.add(font2);
118                    }
119                } else {
120                    // The FontFamily is valid but doesn't contain any matching font. This means
121                    // that the font failed to load. We add null to the list of fonts. Don't throw
122                    // the warning just yet. If this is a non-english font, we don't want to warn
123                    // users who are trying to render only english text.
124                    fonts.add(null);
125                }
126            }
127        }
128        return fonts;
129    }
130
131    /**
132     * Clear the default typefaces when disposing bridge.
133     */
134    public static void resetDefaults() {
135        // Sometimes this is called before the Bridge is initialized. In that case, we don't want to
136        // initialize Typeface because the SDK fonts location hasn't been set.
137        if (FontFamily_Delegate.getFontLocation() != null) {
138            Typeface.sDefaults = null;
139        }
140    }
141
142
143    // ---- native methods ----
144
145    @LayoutlibDelegate
146    /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
147        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
148        if (delegate == null) {
149            delegate = sManager.getDelegate(sDefaultTypeface);
150        }
151        if (delegate == null) {
152            return 0;
153        }
154
155        return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style,
156                delegate.mWeight));
157    }
158
159    @LayoutlibDelegate
160    /*package*/ static long nativeCreateWeightAlias(long native_instance, int weight) {
161        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
162        if (delegate == null) {
163            delegate = sManager.getDelegate(sDefaultTypeface);
164        }
165        if (delegate == null) {
166            return 0;
167        }
168        Typeface_Delegate weightAlias =
169                new Typeface_Delegate(delegate.mFontFamilies, delegate.mStyle, weight);
170        return sManager.addNewDelegate(weightAlias);
171    }
172
173    @LayoutlibDelegate
174    /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) {
175        FontFamily_Delegate[] fontFamilies = new FontFamily_Delegate[familyArray.length];
176        for (int i = 0; i < familyArray.length; i++) {
177            fontFamilies[i] = FontFamily_Delegate.getDelegate(familyArray[i]);
178        }
179        Typeface_Delegate delegate = new Typeface_Delegate(fontFamilies, Typeface.NORMAL);
180        return sManager.addNewDelegate(delegate);
181    }
182
183    @LayoutlibDelegate
184    /*package*/ static void nativeUnref(long native_instance) {
185        sManager.removeJavaReferenceFor(native_instance);
186    }
187
188    @LayoutlibDelegate
189    /*package*/ static int nativeGetStyle(long native_instance) {
190        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
191        if (delegate == null) {
192            return 0;
193        }
194
195        return delegate.mStyle;
196    }
197
198    @LayoutlibDelegate
199    /*package*/ static void nativeSetDefault(long native_instance) {
200        sDefaultTypeface = native_instance;
201    }
202
203    @LayoutlibDelegate
204    /*package*/ static File getSystemFontConfigLocation() {
205        return new File(getFontLocation());
206    }
207
208    // ---- Private delegate/helper methods ----
209
210    private Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style) {
211        this(fontFamilies, style, FontFamily_Delegate.DEFAULT_FONT_WEIGHT);
212    }
213
214    public Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style, int weight) {
215        mFontFamilies = fontFamilies;
216        mStyle = style;
217        mWeight = weight;
218    }
219}
220