Android - 检索默认系统字体字符串名称

时间:2016-09-26 16:31:57

标签: android

对于某些类型的Android(三星,LG,HTC),看起来有可能set the default system font without rooting。我没有其中一种设备可以测试(我有一台较旧的联想平板电脑),但我想知道以下内容:

  1. Typeface.DEFAULT方法是否在这些设备上返回自定义字体的字体,或者始终在Roboto上返回信息?
  2. 如果它确实返回自定义字体的信息,我如何以编程方式获取自定义字体的字体名称(字符串)?
  3. 如果Typeface.DEFAULT是一个死胡同,是否有另一种方法来获取默认的系统字体名称?也许在寻找TTF文件?
  4. 编辑:我添加了一种我认为可行的方法,但实际上并不是这样:将Typeface.DEFAULT与从/system/fonts/system/font和{中的文件创建的Typeface对象进行比较{1}}目录。 这不是一个答案,但它可能有助于刺激某人提出一个。另请注意,TTFAnalyzer类我的。代码如下:

    /data/fonts

    编辑2:在我的联想平板电脑上浏览更多信息。在private String getDefaultFont() { String[] fontdirs = { "/system/fonts", "/system/font", "/data/fonts" }; TTFAnalyzer analyzer = new TTFAnalyzer(); Typeface tfDefault = Typeface.DEFAULT; Typeface tfTemp = null; String defaultFontName = ""; System.out.println("getDefaultFont(): entry"); System.out.println("tfDefault: " + tfDefault.toString()); for ( String fontdir : fontdirs ) { File dir = new File( fontdir ); if ( !dir.exists() ) continue; File[] files = dir.listFiles(); if ( files == null ) continue; for ( File file : files ) { String fontname = analyzer.getTtfFontName( file.getAbsolutePath() ); if ( fontname != null ) { System.out.println("found font: " + fontname); tfTemp = Typeface.createFromFile(file); System.out.println("tfTemp: " + tfTemp.toString()); //** THIS SHOULD BE WORKING? **// if (tfTemp.equals(tfDefault)) { System.out.println("Found default font: " + fontname); defaultFontName = fontname; } } } } return defaultFontName; } // 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 if ( version != 0x74727565 && version != 0x00010000 ) 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; } } 中,有几个感兴趣的xml文件:

    • /system/etc - 看起来它有普通/斜体/粗体的默认字体作为其第一个家庭条目
    • system_fonts.xml - 如果无法找到当前字体中的字形(例如,泰语字符),那么Android应该会重新使用这些字体。

    可能值得解析一下system_fonts并从那里返回默认字体名称 - 但我不知道这是否是“正确”的方法。

1 个答案:

答案 0 :(得分:4)

好的,这是我觉得有用的东西。至于它的Android是否获得批准,嗯,这有点像黑客。这个解决方案假设了一些事情:

  • 文件/system/etc/system_fonts.xml包含设备上的字体列表。
  • <file>文件中的第一个system_fonts.xml元素是设备的默认字体。
  • 默认字体位于/system/fonts

这些假设来自于浏览Android 5.x中的Typeface类文件,但我已经在其他几个版本上进行了测试,解决方案似乎也适用于那里。代码如下(假设TTFAnalyzer类,如上所列):

import android.util.Xml;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

public String getDefaultFont() {
    System.out.println("getFontList(): entry");
    File configFilename = new File("/system/etc/system_fonts.xml");
    String defaultFontName = "";
    TTFAnalyzer analyzer = new TTFAnalyzer();

    try {
        FileInputStream fontsIn = new FileInputStream(configFilename);
        XmlPullParser parser = Xml.newPullParser();
        parser.setInput(fontsIn, null);
        Boolean done = false;
        Boolean getTheText = false;
        int eventType;
        String defaultFont = "";
        while (!done) {
            eventType = parser.next();
            if (eventType == parser.START_TAG && parser.getName().equalsIgnoreCase("file")) {
                // the text is next up -- pull it
                getTheText = true;
            }
            if (eventType == parser.TEXT && getTheText == true) {
                // first name
                defaultFont = parser.getText();
                System.out.println("text for file tag:" + defaultFont);
                done = true;
            }
            if (eventType == parser.END_DOCUMENT) {
                System.out.println("hit end of system_fonts.xml document");
                done = true;
            }
        }

        if (defaultFont.length() > 0) {
            // found the font filename, most likely in /system/fonts. Now pull out the human-readable
            // string from the font file
            System.out.println("Figuring out default Font info");
            String fontname = analyzer.getTtfFontName("/system/fonts/" + defaultFont);
            if ( fontname != null ) {
                System.out.println("found font info: " + fontname);
                defaultFontName = fontname;
            }                
        }

    } catch (RuntimeException e) {
        System.err.println("Didn't create default family (most likely, non-Minikin build)");
        // TODO: normal in non-Minikin case, remove or make error when Minikin-only
    } catch (FileNotFoundException e) {
        System.err.println("GetDefaultFont: config file Not found");
    } catch (IOException e) {
        System.err.println("GetDefaultFont: IO exception: " + e.getMessage());
    } catch (XmlPullParserException e) {
        System.err.println("getDefaultFont: XML parse exception " + e.getMessage());
    }
    return defaultFontName; 
}