From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / android / xscreensaver / src / org / jwz / xscreensaver / TTFAnalyzer.java
diff --git a/android/xscreensaver/src/org/jwz/xscreensaver/TTFAnalyzer.java b/android/xscreensaver/src/org/jwz/xscreensaver/TTFAnalyzer.java
new file mode 100644 (file)
index 0000000..3d01345
--- /dev/null
@@ -0,0 +1,153 @@
+/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+
+ * Copyright (C) 2011 George Yunaev @ Ulduzsoft
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+
+  http://www.ulduzsoft.com/2012/01/enumerating-the-fonts-on-android-platform/
+ */
+package org.jwz.xscreensaver;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.HashMap;
+// The class which loads the TTF file, parses it and returns the TTF font name
+class TTFAnalyzer
+{
+    // This function parses the TTF file and returns the font name specified in the file
+    public String getTtfFontName( String fontFilename )
+    {
+        try
+        {
+            // Parses the TTF file format.
+            // See http://developer.apple.com/fonts/ttrefman/rm06/Chap6.html
+            m_file = new RandomAccessFile( fontFilename, "r" );
+            // Read the version first
+            int version = readDword();
+            // 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();
+            // Skip the rest in the header
+            readWord(); // skip searchRange
+            readWord(); // skip entrySelector
+            readWord(); // skip rangeShift
+            // Now we can read the tables
+            for ( int i = 0; i < numTables; i++ )
+            {
+                // Read the table entry
+                int tag = readDword();
+                readDword(); // skip checksum
+                int offset = readDword();
+                int length = readDword();
+                // 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 ];
+                    m_file.seek( offset );
+                    read( 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;
+        }
+    }
+    // Font file; must be seekable
+    private RandomAccessFile m_file = null;
+    // Helper I/O functions
+    private int readByte() throws IOException
+    {
+        return m_file.read() & 0xFF;
+    }
+    private int readWord() throws IOException
+    {
+        int b1 = readByte();
+        int b2 = readByte();
+        return b1 << 8 | b2;
+    }
+    private int readDword() throws IOException
+    {
+        int b1 = readByte();
+        int b2 = readByte();
+        int b3 = readByte();
+        int b4 = readByte();
+        return b1 << 24 | b2 << 16 | b3 << 8 | b4;
+    }
+    private void read( byte [] array ) throws IOException
+    {
+        if ( m_file.read( array ) != array.length )
+            throw new IOException();
+    }
+    // Helper
+    private int getWord( byte [] array, int offset )
+    {
+        int b1 = array[ offset ] & 0xFF;
+        int b2 = array[ offset + 1 ] & 0xFF;
+        return b1 << 8 | b2;
+    }
+}