11a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien/*
21a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * Copyright (C) 2014 The Android Open Source Project
31a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien *
41a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * Licensed under the Apache License, Version 2.0 (the "License");
51a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * you may not use this file except in compliance with the License.
61a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * You may obtain a copy of the License at
71a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien *
81a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien *      http://www.apache.org/licenses/LICENSE-2.0
91a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien *
101a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * Unless required by applicable law or agreed to in writing, software
111a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * distributed under the License is distributed on an "AS IS" BASIS,
121a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * See the License for the specific language governing permissions and
141a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * limitations under the License.
151a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien */
161a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien
171a73f732f91e97c9c66b808c245ddda36a10e987Raph Levienpackage android.graphics;
181a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien
1999975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournaderimport android.annotation.Nullable;
20d573794d83a049fe59e289944f0cd77406dd776aRaph Levienimport android.content.res.AssetManager;
21ff55115121a7a2753ba2265cb3201a3a14c0874dSeigo Nonakaimport android.graphics.fonts.FontVariationAxis;
2299975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournaderimport android.text.TextUtils;
23296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levienimport android.util.Log;
2446dd24a710ee93ef9d75ea74e8a196e213f2e1daSiyamed Sinir
258b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonakaimport dalvik.annotation.optimization.CriticalNative;
26d573794d83a049fe59e289944f0cd77406dd776aRaph Levien
27abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonakaimport libcore.util.NativeAllocationRegistry;
28abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka
29296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levienimport java.io.FileInputStream;
30296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levienimport java.io.IOException;
31fb95699364e555148b437cfa1e5c69384f843845Ben Wagnerimport java.nio.ByteBuffer;
32296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levienimport java.nio.channels.FileChannel;
33a87b07d7fafd59ae26073a80cd742b17ea427ecdBen Wagner
341a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien/**
351a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * A family of typefaces with different styles.
361a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien *
371a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien * @hide
381a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien */
391a73f732f91e97c9c66b808c245ddda36a10e987Raph Levienpublic class FontFamily {
40296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levien
41296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levien    private static String TAG = "FontFamily";
42296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levien
43abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka    private static final NativeAllocationRegistry sBuilderRegistry = new NativeAllocationRegistry(
44abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka            FontFamily.class.getClassLoader(), nGetBuilderReleaseFunc(), 64);
45abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka
46abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka    private @Nullable Runnable mNativeBuilderCleaner;
47abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka
48abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka    private static final NativeAllocationRegistry sFamilyRegistry = new NativeAllocationRegistry(
49abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka            FontFamily.class.getClassLoader(), nGetFamilyReleaseFunc(), 64);
50abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka
519a5b61ccc83303ceeec2059f58c1977af9faa9e3Raph Levien    /**
529a5b61ccc83303ceeec2059f58c1977af9faa9e3Raph Levien     * @hide
539a5b61ccc83303ceeec2059f58c1977af9faa9e3Raph Levien     */
541a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien    public long mNativePtr;
559a5b61ccc83303ceeec2059f58c1977af9faa9e3Raph Levien
568b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka    // Points native font family builder. Must be zero after freezing this family.
578b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka    private long mBuilderPtr;
588b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka
591a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien    public FontFamily() {
608b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        mBuilderPtr = nInitBuilder(null, 0);
61abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka        mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr);
62f9e3d311275c37fe5f2562993687a1627780a6d0Raph Levien    }
63f9e3d311275c37fe5f2562993687a1627780a6d0Raph Levien
6499975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournader    public FontFamily(@Nullable String[] langs, int variant) {
6599975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournader        final String langsString;
6699975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournader        if (langs == null || langs.length == 0) {
6799975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournader            langsString = null;
6899975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournader        } else if (langs.length == 1) {
6999975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournader            langsString = langs[0];
7099975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournader        } else {
7199975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournader            langsString = TextUtils.join(",", langs);
7299975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournader        }
7399975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournader        mBuilderPtr = nInitBuilder(langsString, variant);
74abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka        mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr);
758b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka    }
768b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka
775b6347a6af668ba47b3fab14cefbe03cc440c3a4Seigo Nonaka    /**
785b6347a6af668ba47b3fab14cefbe03cc440c3a4Seigo Nonaka     * Finalize the FontFamily creation.
795b6347a6af668ba47b3fab14cefbe03cc440c3a4Seigo Nonaka     *
805b6347a6af668ba47b3fab14cefbe03cc440c3a4Seigo Nonaka     * @return boolean returns false if some error happens in native code, e.g. broken font file is
815b6347a6af668ba47b3fab14cefbe03cc440c3a4Seigo Nonaka     *                 passed, etc.
825b6347a6af668ba47b3fab14cefbe03cc440c3a4Seigo Nonaka     */
835b6347a6af668ba47b3fab14cefbe03cc440c3a4Seigo Nonaka    public boolean freeze() {
848b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        if (mBuilderPtr == 0) {
858b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka            throw new IllegalStateException("This FontFamily is already frozen");
868b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        }
878b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        mNativePtr = nCreateFamily(mBuilderPtr);
88abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka        mNativeBuilderCleaner.run();
898b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        mBuilderPtr = 0;
90abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka        if (mNativePtr != 0) {
91abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka            sFamilyRegistry.registerNativeAllocation(this, mNativePtr);
92abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka        }
935b6347a6af668ba47b3fab14cefbe03cc440c3a4Seigo Nonaka        return mNativePtr != 0;
948b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka    }
958b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka
968b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka    public void abortCreation() {
978b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        if (mBuilderPtr == 0) {
988b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka            throw new IllegalStateException("This FontFamily is already frozen or abandoned");
999a5b61ccc83303ceeec2059f58c1977af9faa9e3Raph Levien        }
100abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka        mNativeBuilderCleaner.run();
1018b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        mBuilderPtr = 0;
1021a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien    }
10315cf4757dc0099301662f8a26da561434cc07cfaRaph Levien
104ff55115121a7a2753ba2265cb3201a3a14c0874dSeigo Nonaka    public boolean addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight,
10520e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka            int italic) {
1068b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        if (mBuilderPtr == 0) {
1078b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka            throw new IllegalStateException("Unable to call addFont after freezing.");
1088b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        }
109296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levien        try (FileInputStream file = new FileInputStream(path)) {
110296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levien            FileChannel fileChannel = file.getChannel();
111296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levien            long fontSize = fileChannel.size();
112296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levien            ByteBuffer fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
11320e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka            if (axes != null) {
114ff55115121a7a2753ba2265cb3201a3a14c0874dSeigo Nonaka                for (FontVariationAxis axis : axes) {
115ff55115121a7a2753ba2265cb3201a3a14c0874dSeigo Nonaka                    nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
11620e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka                }
11720e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka            }
11820e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka            return nAddFont(mBuilderPtr, fontBuffer, ttcIndex, weight, italic);
119296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levien        } catch (IOException e) {
120296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levien            Log.e(TAG, "Error mapping font file " + path);
121296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levien            return false;
122296bf8c55aaba0025f3e5b904fda3b6e15686753Raph Levien        }
123d573794d83a049fe59e289944f0cd77406dd776aRaph Levien    }
124d573794d83a049fe59e289944f0cd77406dd776aRaph Levien
125ff55115121a7a2753ba2265cb3201a3a14c0874dSeigo Nonaka    public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes,
12620e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka            int weight, int italic) {
1278b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        if (mBuilderPtr == 0) {
1288b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka            throw new IllegalStateException("Unable to call addFontWeightStyle after freezing.");
1298b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        }
13020e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka        if (axes != null) {
131ff55115121a7a2753ba2265cb3201a3a14c0874dSeigo Nonaka            for (FontVariationAxis axis : axes) {
132ff55115121a7a2753ba2265cb3201a3a14c0874dSeigo Nonaka                nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
13320e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka            }
134ac873c9f25d2a687c9195226b9d680f51c91fa30Seigo Nonaka        }
13520e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka        return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, weight, italic);
136117cbebe810613d4a6de034f02652cdbbfef4cdeRaph Levien    }
137117cbebe810613d4a6de034f02652cdbbfef4cdeRaph Levien
138b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri    /**
139b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri     * @param mgr The AssetManager to use for this context.
140b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri     * @param path The path to the font file to load.
141b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri     * @param cookie If available, the resource cookie given by Resources.
142b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri     * @param isAsset {@code true} if this is from the assets/ folder, {@code false} if from
143b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri     *            resources
144b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri     * @param weight The weight of the font. If 0 is given, the weight and italic will be resolved
145b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri     *            using the OS/2 table in the font.
146b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri     * @param isItalic Whether this font is italic. If the weight is set to 0, this will be resolved
147b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri     *            using the OS/2 table in the font.
148b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri     * @return
149b44abf290190ceee037f24c47493a34de45fa3f4Clara Bayarri     */
15018e9f9f3778318918c44d944489cb50daaf45d1cClara Bayarri    public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie,
15120e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka            boolean isAsset, int ttcIndex, int weight, int isItalic,
152ff55115121a7a2753ba2265cb3201a3a14c0874dSeigo Nonaka            FontVariationAxis[] axes) {
1538b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        if (mBuilderPtr == 0) {
1548b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka            throw new IllegalStateException("Unable to call addFontFromAsset after freezing.");
1558b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka        }
15620e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka        if (axes != null) {
157ff55115121a7a2753ba2265cb3201a3a14c0874dSeigo Nonaka            for (FontVariationAxis axis : axes) {
158ff55115121a7a2753ba2265cb3201a3a14c0874dSeigo Nonaka                nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
15920e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka            }
16020e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka        }
16120e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka        return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, ttcIndex, weight,
16220e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka                isItalic);
1631a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien    }
1641a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien
1652660aca4912b4bd890c5c76688f67f1898dc7b97Seigo Nonaka    // TODO: Remove once internal user stop using private API.
1662660aca4912b4bd890c5c76688f67f1898dc7b97Seigo Nonaka    private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
1672660aca4912b4bd890c5c76688f67f1898dc7b97Seigo Nonaka        return nAddFont(builderPtr, font, ttcIndex, -1, -1);
1682660aca4912b4bd890c5c76688f67f1898dc7b97Seigo Nonaka    }
1692660aca4912b4bd890c5c76688f67f1898dc7b97Seigo Nonaka
17099975a3e25e11f5c5958d8a901d37902087c75fdRoozbeh Pournader    private static native long nInitBuilder(String langs, int variant);
1718b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka
1728b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka    @CriticalNative
1738b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka    private static native long nCreateFamily(long mBuilderPtr);
1748b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka
1758b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka    @CriticalNative
176abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka    private static native long nGetBuilderReleaseFunc();
1778b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka
1788b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka    @CriticalNative
179abe50314163dcc8faf05cf21d649899146bbdbdcSeigo Nonaka    private static native long nGetFamilyReleaseFunc();
18020e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka    // By passing -1 to weigth argument, the weight value is resolved by OS/2 table in the font.
18120e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka    // By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font.
18220e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka    private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex,
18320e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka            int weight, int isItalic);
1848b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka    private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
18520e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka            int ttcIndex, int weight, int isItalic);
1868b48e624457e438fcc2b6b9363329036ef2f7743Seigo Nonaka    private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr,
18720e5d91739fb88a02afb4888bf9f938308bc9b7bSeigo Nonaka            String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic);
188ac873c9f25d2a687c9195226b9d680f51c91fa30Seigo Nonaka
189ac873c9f25d2a687c9195226b9d680f51c91fa30Seigo Nonaka    // The added axis values are only valid for the next nAddFont* method call.
190ac873c9f25d2a687c9195226b9d680f51c91fa30Seigo Nonaka    @CriticalNative
191ac873c9f25d2a687c9195226b9d680f51c91fa30Seigo Nonaka    private static native void nAddAxisValue(long builderPtr, int tag, float value);
1921a73f732f91e97c9c66b808c245ddda36a10e987Raph Levien}
193