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