182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/*
2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project
382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Licensed under the Apache License, Version 2.0 (the "License");
582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * you may not use this file except in compliance with the License.
682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * You may obtain a copy of the License at
782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *      http://www.apache.org/licenses/LICENSE-2.0
982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
1082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Unless required by applicable law or agreed to in writing, software
1182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * distributed under the License is distributed on an "AS IS" BASIS,
1282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * See the License for the specific language governing permissions and
1482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * limitations under the License.
1582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */
16ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.emoji.text;
1782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
18ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
1982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
2082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.content.res.AssetManager;
2138746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikas
22ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.AnyThread;
23ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.IntRange;
24ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RequiresApi;
25ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RestrictTo;
2638746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikasimport androidx.text.emoji.flatbuffer.MetadataList;
2782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
2882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport java.io.IOException;
2982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport java.io.InputStream;
3082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport java.nio.ByteBuffer;
3182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport java.nio.ByteOrder;
3282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
3382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/**
3482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Reads the emoji metadata from a given InputStream or ByteBuffer.
3582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
3682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide
3782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */
3882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir@RestrictTo(LIBRARY_GROUP)
3982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir@AnyThread
4077b5c5b734f9f665577d1e3d178615db43ae1d4fSiyamed Sinir@RequiresApi(19)
4182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirclass MetadataListReader {
4282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
4382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
4482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Meta tag for emoji metadata. This string is used by the font update script to insert the
4582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * emoji meta into the font. This meta table contains the list of all emojis which are stored in
4682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * binary format using FlatBuffers. This flat list is later converted by the system into a trie.
47894f169362c12ba9424214a075d2fa2cc255fb37Siyamed Sinir     * {@code int} representation for "Emji"
4882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
4982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @see MetadataRepo
5082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
51894f169362c12ba9424214a075d2fa2cc255fb37Siyamed Sinir    private static final int EMJI_TAG = 'E' << 24 | 'm' << 16 | 'j' << 8 | 'i';
5282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
5382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
54a03c41155d5cc859cb8c0761a6e47c667593dd69Siyamed Sinir     * Deprecated meta tag name. Do not use, kept for compatibility reasons, will be removed soon.
55a03c41155d5cc859cb8c0761a6e47c667593dd69Siyamed Sinir     */
56a03c41155d5cc859cb8c0761a6e47c667593dd69Siyamed Sinir    private static final int EMJI_TAG_DEPRECATED = 'e' << 24 | 'm' << 16 | 'j' << 8 | 'i';
57a03c41155d5cc859cb8c0761a6e47c667593dd69Siyamed Sinir
58a03c41155d5cc859cb8c0761a6e47c667593dd69Siyamed Sinir    /**
5982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * The name of the meta table in the font. int representation for "meta"
6082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
61894f169362c12ba9424214a075d2fa2cc255fb37Siyamed Sinir    private static final int META_TABLE_NAME = 'm' << 24 | 'e' << 16 | 't' << 8 | 'a';
6282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
6382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
6482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Construct MetadataList from an input stream. Does not close the given InputStream, therefore
6582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * it is caller's responsibility to properly close the stream.
6682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
6782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param inputStream InputStream to read emoji metadata from
6882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
6982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    static MetadataList read(InputStream inputStream) throws IOException {
7082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final OpenTypeReader openTypeReader = new InputStreamOpenTypeReader(inputStream);
7182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final OffsetInfo offsetInfo = findOffsetInfo(openTypeReader);
7282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        // skip to where metadata is
7382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        openTypeReader.skip((int) (offsetInfo.getStartOffset() - openTypeReader.getPosition()));
7482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        // allocate a ByteBuffer and read into it since FlatBuffers can read only from a ByteBuffer
7582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final ByteBuffer buffer = ByteBuffer.allocate((int) offsetInfo.getLength());
7682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final int numRead = inputStream.read(buffer.array());
7782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        if (numRead != offsetInfo.getLength()) {
7882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            throw new IOException("Needed " + offsetInfo.getLength() + " bytes, got " + numRead);
7982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
8082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
8182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return MetadataList.getRootAsMetadataList(buffer);
8282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
8382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
8482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
8582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Construct MetadataList from a byte buffer.
8682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
8782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param byteBuffer ByteBuffer to read emoji metadata from
8882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
8982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    static MetadataList read(final ByteBuffer byteBuffer) throws IOException {
9082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final ByteBuffer newBuffer = byteBuffer.duplicate();
9182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final OpenTypeReader reader = new ByteBufferReader(newBuffer);
9282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final OffsetInfo offsetInfo = findOffsetInfo(reader);
9382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        // skip to where metadata is
9482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        newBuffer.position((int) offsetInfo.getStartOffset());
9582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return MetadataList.getRootAsMetadataList(newBuffer);
9682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
9782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
9882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
9982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Construct MetadataList from an asset.
10082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
10182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param assetManager AssetManager instance
10282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param assetPath asset manager path of the file that the Typeface and metadata will be
10382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *                  created from
10482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
10582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    static MetadataList read(AssetManager assetManager, String assetPath)
10682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            throws IOException {
107484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir        try (InputStream inputStream = assetManager.open(assetPath)) {
10882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return read(inputStream);
10982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
11082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
11182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
11282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
11382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Finds the start offset and length of the emoji metadata in the font.
11482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
11582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @return OffsetInfo which contains start offset and length of the emoji metadata in the font
11682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
11782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @throws IOException
11882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
11982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static OffsetInfo findOffsetInfo(OpenTypeReader reader) throws IOException {
12082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        // skip sfnt version
12182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        reader.skip(OpenTypeReader.UINT32_BYTE_COUNT);
12282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        // start of Table Count
12382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final int tableCount = reader.readUnsignedShort();
12482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        if (tableCount > 100) {
12582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            //something is wrong quit
12682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            throw new IOException("Cannot read metadata.");
12782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
12882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        //skip to begining of tables data
12982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        reader.skip(OpenTypeReader.UINT16_BYTE_COUNT * 3);
13082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
13182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        long metaOffset = -1;
13282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        for (int i = 0; i < tableCount; i++) {
13382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            final int tag = reader.readTag();
13482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            // skip checksum
13582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            reader.skip(OpenTypeReader.UINT32_BYTE_COUNT);
13682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            final long offset = reader.readUnsignedInt();
13782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            // skip mLength
13882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            reader.skip(OpenTypeReader.UINT32_BYTE_COUNT);
13982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (META_TABLE_NAME == tag) {
14082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                metaOffset = offset;
14182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                break;
14282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
14382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
14482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
14582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        if (metaOffset != -1) {
14682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            // skip to the begining of meta tables.
14782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            reader.skip((int) (metaOffset - reader.getPosition()));
14882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            // skip minorVersion, majorVersion, flags, reserved,
14982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            reader.skip(
15082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    OpenTypeReader.UINT16_BYTE_COUNT * 2 + OpenTypeReader.UINT32_BYTE_COUNT * 2);
15182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            final long mapsCount = reader.readUnsignedInt();
15282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            for (int i = 0; i < mapsCount; i++) {
15382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                final int tag = reader.readTag();
15482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                final long dataOffset = reader.readUnsignedInt();
15582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                final long dataLength = reader.readUnsignedInt();
156a03c41155d5cc859cb8c0761a6e47c667593dd69Siyamed Sinir                if (EMJI_TAG == tag || EMJI_TAG_DEPRECATED == tag) {
15782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    return new OffsetInfo(dataOffset + metaOffset, dataLength);
15882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                }
15982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
16082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
16182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
16282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        throw new IOException("Cannot read metadata.");
16382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
16482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
16582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
16682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Start offset and length of the emoji metadata in the font.
16782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
16882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static class OffsetInfo {
16982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final long mStartOffset;
17082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final long mLength;
17182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
17282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        OffsetInfo(long startOffset, long length) {
17382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mStartOffset = startOffset;
17482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mLength = length;
17582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
17682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
17782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        long getStartOffset() {
17882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mStartOffset;
17982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
18082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
18182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        long getLength() {
18282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mLength;
18382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
18482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
18582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
186fb15b88b8ff336c2f8c2dff88e55eaba3491349aSiyamed Sinir    private static int toUnsignedShort(final short value) {
18782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return value & 0xFFFF;
18882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
18982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
190fb15b88b8ff336c2f8c2dff88e55eaba3491349aSiyamed Sinir    private static long toUnsignedInt(final int value) {
19182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return value & 0xFFFFFFFFL;
19282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
19382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
19482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private interface OpenTypeReader {
19582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        int UINT16_BYTE_COUNT = 2;
19682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        int UINT32_BYTE_COUNT = 4;
19782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
19882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        /**
19982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * Reads an {@code OpenType uint16}.
20082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         *
20182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * @throws IOException
20282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         */
20382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        int readUnsignedShort() throws IOException;
20482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
20582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        /**
20682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * Reads an {@code OpenType uint32}.
20782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         *
20882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * @throws IOException
20982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         */
21082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        long readUnsignedInt() throws IOException;
21182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
21282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        /**
21382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * Reads an {@code OpenType Tag}.
21482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         *
21582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * @throws IOException
21682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         */
21782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        int readTag() throws IOException;
21882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
21982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        /**
22082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * Skip the given amount of numOfBytes
22182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         *
22282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * @throws IOException
22382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         */
22482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        void skip(int numOfBytes) throws IOException;
22582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
22682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        /**
22782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * @return the position of the reader
22882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         */
22982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        long getPosition();
23082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
23182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
23282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
23382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Reads {@code OpenType} data from an {@link InputStream}.
23482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
23582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static class InputStreamOpenTypeReader implements OpenTypeReader {
23682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
23782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final byte[] mByteArray;
23882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final ByteBuffer mByteBuffer;
23982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final InputStream mInputStream;
24082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private long mPosition = 0;
24182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
24282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        /**
24382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * Constructs the reader with the given InputStream. Does not close the InputStream, it is
24482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * caller's responsibility to close it.
24582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         *
24682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * @param inputStream InputStream to read from
24782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         */
24882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        InputStreamOpenTypeReader(final InputStream inputStream) {
24982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mInputStream = inputStream;
25082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mByteArray = new byte[UINT32_BYTE_COUNT];
25182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mByteBuffer = ByteBuffer.wrap(mByteArray);
25282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mByteBuffer.order(ByteOrder.BIG_ENDIAN);
25382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
25482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
25582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
25682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public int readUnsignedShort() throws IOException {
25782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mByteBuffer.position(0);
25882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            read(UINT16_BYTE_COUNT);
25982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return toUnsignedShort(mByteBuffer.getShort());
26082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
26182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
26282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
26382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public long readUnsignedInt() throws IOException {
26482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mByteBuffer.position(0);
26582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            read(UINT32_BYTE_COUNT);
26682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return toUnsignedInt(mByteBuffer.getInt());
26782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
26882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
26982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
27082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public int readTag() throws IOException {
27182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mByteBuffer.position(0);
27282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            read(UINT32_BYTE_COUNT);
27382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mByteBuffer.getInt();
27482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
27582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
27682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
27782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public void skip(int numOfBytes) throws IOException {
27882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            while (numOfBytes > 0) {
279147eeb334b743f9510368a2ff9483ba2fec6340aAurimas Liutikas                int skipped = (int) mInputStream.skip(numOfBytes);
28082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                if (skipped < 1) {
28182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    throw new IOException("Skip didn't move at least 1 byte forward");
28282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                }
28382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                numOfBytes -= skipped;
28482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mPosition += skipped;
28582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
28682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
28782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
28882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
28982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public long getPosition() {
29082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mPosition;
29182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
29282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
29382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private void read(@IntRange(from = 0, to = UINT32_BYTE_COUNT) final int numOfBytes)
29482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                throws IOException {
29582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (mInputStream.read(mByteArray, 0, numOfBytes) != numOfBytes) {
29682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                throw new IOException("read failed");
29782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
29882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mPosition += numOfBytes;
29982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
30082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
30182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
30282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
30382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Reads OpenType data from a ByteBuffer.
30482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
30582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static class ByteBufferReader implements OpenTypeReader {
30682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
30782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final ByteBuffer mByteBuffer;
30882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
30982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        /**
31082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * Constructs the reader with the given ByteBuffer.
31182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         *
31282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         * @param byteBuffer ByteBuffer to read from
31382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir         */
31482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        ByteBufferReader(final ByteBuffer byteBuffer) {
31582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mByteBuffer = byteBuffer;
31682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mByteBuffer.order(ByteOrder.BIG_ENDIAN);
31782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
31882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
31982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
32082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public int readUnsignedShort() throws IOException {
32182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return toUnsignedShort(mByteBuffer.getShort());
32282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
32382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
32482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
32582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public long readUnsignedInt() throws IOException {
32682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return toUnsignedInt(mByteBuffer.getInt());
32782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
32882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
32982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
33082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public int readTag() throws IOException {
33182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mByteBuffer.getInt();
33282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
33382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
33482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
33582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public void skip(final int numOfBytes) throws IOException {
33682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mByteBuffer.position(mByteBuffer.position() + numOfBytes);
33782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
33882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
33982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
34082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public long getPosition() {
34182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mByteBuffer.position();
34282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
34382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
3440f4ca634bbc43ddff900c35f7d2a43b55d8c830dJake Wharton
3450f4ca634bbc43ddff900c35f7d2a43b55d8c830dJake Wharton    private MetadataListReader() {
3460f4ca634bbc43ddff900c35f7d2a43b55d8c830dJake Wharton    }
34782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir}
348