2
votes

I have written a simple code to check whether a glyph available in given custom font file (.ttf or .otf), for this purpose I am using:

Font.canDisplay()

when I tested my code on windows its working fine, but on mac it always returns true even if that glyph is not available, I guess the font substitution option is ON by default in mac, any advice how to turn this OFF?

Environment:

VM Version 1.6.0_26
Mac OS X 10.7.2

Font File (truetype): http://www.fontsc.com/font/depth-charge (you can check character map on this website glyphs are not available in CAPs)

2

2 Answers

1
votes

.

Font font = Font.createFont(int fontFormat, File fontFile)
/(int fontFormat, InputStream fontStream)

GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(font);

if (font.canDisplay()/canDisplayUpTo()){
    //your code with font.....    
}
0
votes

UPDATE: Oracle accepted my bug report: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8223834

You will find a possible workaround below:

This seems to be a bug in Mac OS implementation of Font2D class.

I was able to workaround the problem using reflection. Here is a code that produces correct results on Mac. It's in Kotlin, but it should be straightforward to translate it to Java if necessary.

// Mac OS workaround for incorrectly implemented canDisplayUpTo method
fun Font.macCanDisplayUpTo(str: String): Int {
    val getFontMethod = Font::class.java.getDeclaredMethod("getFont2D")
    getFontMethod.isAccessible = true
    val font2d = getFontMethod.invoke(this)
    val getMapperMethod = font2d.javaClass.getDeclaredMethod("getMapper")
    getMapperMethod.isAccessible = true
    val mapper = getMapperMethod.invoke(font2d)
    val charToGlyphMethod = mapper.javaClass.getDeclaredMethod("charToGlyph", Char::class.java)

    val len = str.length
    var i = 0
    while (i < len) {
        val c = str[i]
        val glyph = charToGlyphMethod.invoke(mapper, c) as Int
        if (glyph >= 0) {
            i++
            continue
        }
        if (!Character.isHighSurrogate(c)
                || (charToGlyphMethod.invoke(mapper, str.codePointAt(i)) as Int) < 0) {
            return i
        }
        i += 2
    }
    return -1
}

If you're using Java 9 or newer, you will need to pass these command args to VM to make the code work:

--add-opens java.desktop/java.awt=your.module.name
--add-opens java.desktop/sun.font=your.module.name

This code won't work on Windows (will produce incorrect results). It's for MacOS only.