/*
 * Copyright (C) 2015 Emanuel Moecklin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.onegravity.rteditor.fonts;

import android.content.res.AssetManager;

import org.apache.commons.io.IOUtils;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

/**
 * This class offers methods to analyze ttf files, namely to retrieve the font name.
 */
abstract class TTFAnalyzer {

    /**
     * Retrieve the file name for a system font.
     *
     * @param filePath the full path for the font to retrieve.
     *
     * @return The file name or null of none could be retrieved.
     */
    static String getFontName(String filePath) {
        TTFRandomAccessFile in = null;
        try {
            RandomAccessFile file = new RandomAccessFile(filePath, "r");
            in = new TTFRandomAccessFile(file);
            return getTTFFontName(in, filePath);
        } catch (FileNotFoundException e) {
            return null;    // Missing permissions?
        } catch (IOException e) {
            return null;    // Corrupted font file?
        }
        finally {
            IOUtils.closeQuietly(in);
        }
    }

    /**
     * Retrieve the file name for a font in the asset folder.
     *
     * @param filePath the full path for the font in the asset folder to retrieve.
     *
     * @return The file name or null of none could be retrieved.
     */
    static String getFontName(AssetManager assets, String filePath) {
        TTFAssetInputStream in = null;
        try {
            InputStream file = assets.open(filePath, AssetManager.ACCESS_RANDOM);
            in = new TTFAssetInputStream(file);
            return getTTFFontName(in, filePath);
        } catch (FileNotFoundException e) {
            return null;    // Missing permissions?
        } catch (IOException e) {
            return null;    // Corrupted font file?
        }
        finally {
            IOUtils.closeQuietly(in);
        }
    }

    private static String getTTFFontName(TTFInputStream in, String fontFilename) {
        try {
            // Read the version first
            int version = readDword(in);

            // The version must be either 'true' (0x74727565) or 0x00010000 or 'OTTO' (0x4f54544f) for CFF style fonts.
            if (version != 0x74727565 && version != 0x00010000 && version != 0x4f54544f) {
                return null;
            }

            // The TTF file consist of several sections called "tables", and
            // we need to know how many of them are there.
            int numTables = readWord(in);

            // Skip the rest in the header
            readWord(in); // skip searchRange
            readWord(in); // skip entrySelector
            readWord(in); // skip rangeShift

            // Now we can read the tables
            for (int i = 0; i < numTables; i++) {
                // Read the table entry
                int tag = readDword(in);
                readDword(in); // skip checksum
                int offset = readDword(in);
                int length = readDword(in);

                // Now here' the trick. 'name' field actually contains the
                // textual string name.
                // So the 'name' string in characters equals to 0x6E616D65
                if (tag == 0x6E616D65) {
                    // Here's the name section. Read it completely into the
                    // allocated buffer
                    byte[] table = new byte[length];

                    in.seek(offset);
                    read(in, table);

                    // This is also a table. See
                    // http://developer.apple.com/fonts/ttrefman/rm06/Chap6name.html
                    // According to Table 36, the total number of table
                    // records is stored in the second word, at the offset
                    // 2.
                    // Getting the count and string offset - remembering
                    // it's big endian.
                    int count = getWord(table, 2);
                    int string_offset = getWord(table, 4);

                    // Record starts from offset 6
                    for (int record = 0; record < count; record++) {
                        // Table 37 tells us that each record is 6 words ->
                        // 12 bytes, and that the nameID is 4th word so its
                        // offset is 6.
                        // We also need to account for the first 6 bytes of
                        // the header above (Table 36), so...
                        int nameid_offset = record * 12 + 6;
                        int platformID = getWord(table, nameid_offset);
                        int nameid_value = getWord(table, nameid_offset + 6);

                        // Table 42 lists the valid name Identifiers. We're
                        // interested in 4 but not in Unicode encoding (for
                        // simplicity).
                        // The encoding is stored as PlatformID and we're
                        // interested in Mac encoding
                        if (nameid_value == 4 && platformID == 1) {
                            // We need the string offset and length, which
                            // are the word 6 and 5 respectively
                            int name_length = getWord(table,
                                    nameid_offset + 8);
                            int name_offset = getWord(table,
                                    nameid_offset + 10);

                            // The real name string offset is calculated by
                            // adding the string_offset
                            name_offset = name_offset + string_offset;

                            // Make sure it is inside the array
                            if (name_offset >= 0 && name_offset + name_length < table.length) {
                                return new String(table, name_offset, name_length);
                            }
                        }
                    }
                }
            }

            return null;
        } catch (FileNotFoundException e) {
            // Permissions?
            return null;
        } catch (IOException e) {
            // Most likely a corrupted font file
            return null;
        }
    }

    private static int readByte(TTFInputStream in) throws IOException {
        return in.read() & 0xFF;
    }

    private static int readWord(TTFInputStream in) throws IOException {
        int b1 = readByte(in);
        int b2 = readByte(in);

        return b1 << 8 | b2;
    }

    private static int readDword(TTFInputStream in) throws IOException {
        int b1 = readByte(in);
        int b2 = readByte(in);
        int b3 = readByte(in);
        int b4 = readByte(in);

        return b1 << 24 | b2 << 16 | b3 << 8 | b4;
    }

    private static void read(TTFInputStream in, byte[] array) throws IOException {
        if (in.read(array) != array.length) throw new IOException();
    }

    private static int getWord(byte[] array, int offset) {
        int b1 = array[offset] & 0xFF;
        int b2 = array[offset + 1] & 0xFF;

        return b1 << 8 | b2;
    }

}
@@ -1462,36 +1462,26 @@
    %7D catch (
-FileNotFound
+IO
 Exception e)
@@ -1537,76 +1537,13 @@
 ions
-?%0A        %7D catch (IOException e) %7B%0A            return null;    // C
+ or c
 orru

make exception handling a bit easier