Как использовать пары кернинга, извлеченные из файла TTF, чтобы правильно отображать глифы как Path2D в Java?
Этот вопрос касается восстановления информации о шрифтах глифов в Java и связан с вопросом, размещенным здесь.Для получения более подробной информации, пожалуйста, проверьте вопрос и ответы.
Там было предложено использовать библиотеку Apache FOP для восстановления пар кернинга непосредственно из файла Truetype, поскольку Java не предоставляет эту информацию. Затем я портировал библиотеку в Windows и восстановил пары кернинга, используя этот код:
TTFFile file;
File ttf = new File("C:\\Windows\\Fonts\\calibri.ttf" );
try { file = TTFFile.open(ttf); }
catch (IOException e) {e.printStackTrace(); }
Map<Integer, Map<Integer, Integer>> kerning = file.getKerning();
Наконец, библиотека работает, но возвращенные пары кернинга не работают с глифами, полученными в Path2D.Float с помощью функции ниже и фрагмента кода, показанного сразу после:
void vectorize(Path2D.Float path, String s) {
PathIterator pIter;
FontRenderContext frc = new FontRenderContext(null,true,true);
GlyphVector gv;
Shape glyph;
gv = font.createGlyphVector(frc, s);
glyph = gv.getGlyphOutline(0);
pIter = glyph.getPathIterator(null);
while (!pIter.isDone()) {
switch(pIter.currentSegment(points)) {
case PathIterator.SEG_MOVETO:
path.moveTo(points[0], points[1]);
break;
case PathIterator.SEG_LINETO :
path.lineTo(points[0], points[1]);
break;
case PathIterator.SEG_QUADTO :
path.quadTo(points[0], points[1], points[2], points[3]);
break;
case PathIterator.SEG_CUBICTO :
path.curveTo(points[0], points[1], points[2], points[3], points[4], points[5]);
break;
case PathIterator.SEG_CLOSE :
path.closePath();
}
pIter.next();
}
}
Длины глифов извлекаются в линзу массива:
Font font = new Font("Calibri", Font.PLAIN, 1000);
double interchar = 1000. * 0.075;
int size = '}' - ' ' + 1;
Path2D.Float[] glyphs = new Path2D.Float[size];
double[] lens = new double[size];
String chars[] = new String[size];
int i; char c;
char[] s = { '0' };
for (i = 0, c = ' '; c <= '}'; c++, i++) { s[0] = c; chars[i] = new String(s); }
for (i = 0; i < size; i++) {
vectorize(glyphs[i] = new Path2D.Float(), chars[i]); // function shown above
lens[i] = glyphs[i].getBounds2D().getWidth() + interchar;
}
Чтобы быть ясным, я отображаю глифы, используя заливку из Graphics2D, и я перевожу, используя указанные выше длины, добавленные к смещениям кернинга, возвращаемым библиотекой Apache FOP, как предлагается, но результат ужасен. Размер шрифта стандартный 1000, как было предложено в этом обсуждении, а результат interchar - 75. Все это кажется правильным, но мои пары ручного кернинга выглядят намного лучше, чем использование пар кернинга из файла TTF.
Есть ли кто-нибудь, кто разбирается в этой библиотеке или шрифтах Truetype, чтобы сказать, как мы должны использовать эти пары кернинга?
Требуется ли доступ к глифам непосредственно из файла TTF вместо использования управления шрифтами Java, как показано выше? Если да, то как?
2 ответа
Задача решена!
Напомним, что для открытия файла и получения пар кернинга нужен этот код с использованием библиотеки Apache FOP:
TTFFile file;
File ttf = new File("C:\\Windows\\Fonts\\calibri.ttf" );
try { file = TTFFile.open(ttf); }
catch (IOException e) {e.printStackTrace(); }
Map<Integer, Map<Integer, Integer>> kerning = file.getKerning();
Следующий фрагмент кода для векторизации глифов теперь верен:
Font font = new Font("Calibri", Font.PLAIN, 2048);
int size = '}' - ' ' + 1;
Path2D.Float[] glyphs = new Path2D.Float[size];
//double[] lens = new double[size];
String chars[] = new String[size];
int i; char c;
char[] s = { '0' };
for (i = 0, c = ' '; c <= '}'; c++, i++) { s[0] = c; chars[i] = new String(s); }
for (i = 0; i < size; i++) {
vectorize(glyphs[i] = new Path2D.Float(), chars[i]);
//lens[i] = glyphs[i].getBounds2D().getWidth();
}
Обратите внимание, что теперь размер шрифта составляет 2048, что является единицей PerEm для этого конкретного шрифта. Это значение задается тегом HEAD в файле шрифта, как описано здесь.
Обратите внимание, что ширина не может быть задана массивом
lens
и код, закомментированный выше. Его нужно прочитать из файла. С помощью
int width = getCharWidthRaw(prev)
из Apache FOP, где
prev
это предыдущий символ,
width
необработанная ширина символа, как написано в файле. Это значение необходимо добавить к значению пары кернинга, которое можно получить на карте.
kerning
.
Карта используется так:
kerning.get(prev)
который возвращает другую карту, содержащую символы и значения кернинга, которые нужно добавить. Если символ, который будет показан следующим, найден на этой карте, соответствующее значение добавляется к
width
. Если не найден, или если
null
возвращается, для этой пары нет значения кернинга.
Вот текст, показывающий, что кернинг теперь работает.
GNU Classpath содержит примерgnu.classpath.examples.awt.HintingDemo.java, который может помочь решить эту проблему. Этот пример позволяет вам визуализировать глифы. Он читает шрифт и интерпретирует язык для подсказок, содержащихся в нем. Вы можете выбрать отображение с подсказками или без них (глифы с подсказками подходят для небольших шрифтов, но не рекомендуются для больших). Если вы не привыкли к подсказкам Truetype, с помощью этой демонстрации вы поймете, что они выравнивают пути в пределах целочисленных границ. Программа не очень навороченная, но в ней есть все необходимые инструменты для чтения глифов и интерпретации подсказок с преимуществом визуализации результатов.
Вам не нужен весь пакет для компиляции и запуска этой демонстрации. Если вы используете Eclipse, легко создать для него проект. Сначала создайте пакеты gnu.classpath.examples.awt и импортируйте в них HintingDemo.java. Затем вы просто импортируете все его зависимости, файл за файлом или целые пакеты за раз. Например, вы можете импортировать весь пакет gnu.java.awt.font и стереть OpenTypeFontPeer.java (демонстрации он не нужен, и если вы оставите его, это вызовет ошибку).
Это дает автономный способ чтения и отображения глифов непосредственно из файла шрифта. Что интересно, он не использует никакой информации о кернинге. Это должно быть добавлено с библиотекой Apache FOP. Если чтение файла дважды является проблемой, вам понадобится обходной путь: либо углубиться в GNU Classpath, чтобы получить ту же информацию, либо попытаться заставить Apache FOP "разговаривать" с GNU Classpath. На данный момент я не могу сказать, насколько это сложно. Я использую его только как инструменты для копирования информации и использования в другом месте, а не как способ действительно читать файлы шрифтов в реальной программе. Шрифты очень компактны, но не являются наиболее эффективным способом отображения текста, особенно там, где есть интерпретация языка шрифтов, как в случае шрифтов Type 1 и Truetype. Избавление от этой интерпретации кажется хорошей идеей, если вы хотите высокого качества и скорости.