Проверьте, может ли пользовательский шрифт отображать символ

У меня есть собственный шрифт, который отображает символ окна. Шрифт, который я использую, по-видимому, не поддерживает все языки. Я хочу проверить, будет ли строка, которую я собираюсь отобразить, может отображаться моим пользовательским шрифтом. Если это невозможно, я хочу использовать стандартный шрифт Android (который, как я знаю, может отображать символы). Я не могу найти способ проверить, может ли мой Typeface отображать определенную строку. Я уверен, что видел метод, который делает это где-то. Кто-нибудь знает?

Обновление. Если вы кодируете API-уровень 23 или выше, используйте Paint.hasGlyph (), как указано в ответе Орсона Бейнса . В противном случае см. Мой первоначальный ответ ниже.


Как вы упомянули в своем собственном ответе, для проверки этого не существует встроенных методов. Однако, если у вас есть некоторый контроль над строкой / символами, которые вы хотите проверить, на самом деле это можно сделать с помощью немного более творческого подхода.

Вы можете нарисовать символ, который, как вам известно, отсутствует в шрифте, который вы хотите проверить, а затем нарисовать персонажа, который вы хотите знать, если он существует, а затем, наконец, сравнить их и посмотреть, выглядят ли они одинаково.

Вот пример кода, который иллюстрирует, как я выполнил эту проверку:

public Bitmap drawBitmap(String text){ Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ALPHA_8); Canvas c = new Canvas(b); c.drawText(text, 0, BITMAP_HEIGHT / 2, paint); return b; } public byte[] getPixels(Bitmap b) { ByteBuffer buffer = ByteBuffer.allocate(b.getByteCount()); b.copyPixelsToBuffer(buffer); return buffer.array(); } public boolean isCharacterMissingInFont(String ch) { String missingCharacter = "\u0978"; // reserved code point in the devanagari block (should not exist). byte[] b1 = getPixels(drawBitmap(ch)); byte[] b2 = getPixels(drawBitmap(missingCharacter)); return Arrays.equals(b1, b2); } 

Существуют некоторые важные ограничения, которые следует учитывать при использовании этого метода:

  • Символ, который вы проверяете (аргумент to isCharacterMissing ), должен быть небелым (может быть обнаружен с помощью Character.isWhitespace () ). В некоторых шрифтах отсутствующие символы отображаются как пробельные символы, поэтому, если вы будете сравнивать пробельный символ, который существует, он будет неправильно сообщаться как отсутствующий символ в таких шрифтах.
  • Элемент missingCharacter должен быть символом, который, как известно, отсутствует в проверяемом шрифте. Кодовая точка U + 0978, которую я использую, представляет собой зарезервированную кодовую точку в середине блока Unicode от Devanagari, поэтому она должна отсутствовать во всех совместимых с Unicode шрифтах, однако в будущем, когда новые символы добавляются в Юникод, эта кодовая точка может Присваивается реальный символ. Таким образом, этот подход не является будущим доказательством, но если вы сами предоставите шрифт, вы можете убедиться, что используете отсутствующий символ всякий раз, когда вы меняете шрифт приложения.
  • Поскольку эта проверка выполняется путем рисования растровых изображений и их сравнения, она не очень эффективна, поэтому ее следует использовать только для проверки нескольких символов, а не всех строк в приложении.

Обновить:

Решение взглянуть на U + 0978 не получилось долго, как указывали другие. Этот символ был добавлен в выпуске Unicode 7.0 в июне 2014 года . Другой вариант – использовать глиф, который существует в юникоде, но вряд ли он будет использоваться в обычном шрифте.

U + 124AB в раннем династическом блоке клинописей, вероятно, не существует во многих шрифтах.

Начиная с версии Android 23, вы можете протестировать ее следующим образом:

 Typeface typeface; //initialize the custom font here //enter the character to test String charToTest="\u0978"; Paint paint=new Paint(); paint.setTypeface(typeface); boolean hasGlyph=paint.hasGlyph(charToTest); 

Ответ @nibarius работает хорошо, хотя с другим персонажем: «\ u2936».

Однако это решение тяжело в памяти, процессоре и времени. Мне нужно решение для очень небольшого количества символов, поэтому я реализовал «быстрое и грязное» решение, проверив только границы. Он работает примерно в 50 раз быстрее, поэтому он более подходит для моего случая:

 public boolean isCharGlyphAvailable(char... c) { Rect missingCharBounds = new Rect(); char missingCharacter = '\u2936'; paint.getTextBounds(c, 0, 1, missingCharBounds); // takes array as argument, but I need only one char. Rect testCharsBounds = new Rect(); paint.getTextBounds(c, 0, 1, testCharsBounds); return !testCharsBounds.equals(missingCharBounds); } 

Я должен предупредить, что этот метод менее точен, чем @nibarius, и есть некоторые ложные негативы и ложные срабатывания, но если вам просто нужно проверить несколько символов, чтобы иметь возможность отбрасывать другие символы – это решение отлично.

Проверьте исходный код проекта Googles Mozc . Класс EmojiRenderableChecker, похоже, работает очень хорошо!

Это похоже на совместимую версию Paint.hasGlypgh (добавляется в Marshmallow).