При создании PDF-документов с использованием шрифтов OpenType в iText я хочу получить доступ к вариантам глифов из шрифта - в частности, табличные данные. Поскольку варианты глифов OpenType не имеют индексов Unicode, я не уверен, как указать, что я хочу использовать определенный набор вариантов (табличные данные), или вызвать определенный глиф по его идентификатору глифа. Просто найдите соответствующее имя класса iText, если оно существует.
Доступ к вариантам глифов OpenType в iText
Ответ 1
Это не представляется возможным ни в последнем теге 5.5.8, ни в master ветвь iText.
Как объяснено в этой статье и в Microsoft спецификация файла шрифта OpenType, варианты глифов хранятся в Glyph Substitution Table (GSUB)
файла шрифта. Доступ к вариантам глифов требует чтения этой таблицы из файла, который фактически реализован в классе com.itextpdf.text.pdf.fonts.otf.GlyphSubstitutionTableReader
, хотя этот класс теперь отключен.
Вызов readGsubTable()
в классе com.itextpdf.text.pdf.TrueTypeFontUnicode
закомментирован.
void process(byte ttfAfm[], boolean preload) throws DocumentException, IOException {
super.process(ttfAfm, preload);
//readGsubTable();
}
Оказывается, эта строка отключена по какой-то причине, так как код на самом деле не работает, если вы пытаетесь его активировать.
Итак, к сожалению, нет способа использовать варианты глифов, поскольку информация о замещении никогда не загружается из файла шрифта.
Обновление
Первоначальный ответ заключался в возможности использовать iText API
для доступа к вариантам глифов из коробки, чего еще нет. Тем не менее, код низкого уровня на месте и может быть использован после некоторого взлома для доступа к таблице сопоставления подстановки глифов.
Когда вызывается read()
, GlyphSubstitutionTableReader
читает таблицу GSUB
и выравнивает подстановки всех функций на одну карту Map<Integer, List<Integer>> rawLigatureSubstitutionMap
. Символьные имена функций в настоящее время отбрасываются OpenTypeFontTableReader
. rawLigatureSubstitutionMap
отображает a glyphId
вариант в базу glyphId
или лигатуру glyphId
в последовательность glyphIds
следующим образом:
629 -> 66 // a.feature -> a
715 -> 71, 71, 77 // ffl ligature
Это отображение можно отменить, чтобы получить все варианты для базы glyphId
. Таким образом, все расширенные глифы с неизвестными значениями юникода могут быть выяснены через их соединение с базовым глифом или последовательность глифов.
Далее, чтобы писать глиф в PDF, нам нужно знать значение unicode для этого glyphId
. Связь unicode -> glyphId
отображается в поле cmap31
в TrueTypeFont
. Обратное отображение карты дает unicode с помощью glyphId.
Tweaking
rawLigatureSubstitutionMap
невозможно получить в GlyphSubstitutionTableReader
, так как это член private
и не имеет аксессуар-приемник. Простейшим взломом было бы скопировать-вставить исходный класс и добавить геттер для карты:
public class HackedGlyphSubstitutionTableReader extends OpenTypeFontTableReader {
// copy-pasted code ...
public Map<Integer, List<Integer>> getRawSubstitutionMap() {
return rawLigatureSubstitutionMap;
}
}
Следующая проблема заключается в том, что GlyphSubstitutionTableReader
требуется смещение для таблицы GSUB
, информация, которая хранится в protected HashMap<String, int[]> tables
класса TrueTypeFont
. Класс-помощник, помещенный в один и тот же пакет, позволит получить доступ к защищенным членам TrueTypeFont
.
package com.itextpdf.text.pdf;
import com.itextpdf.text.pdf.fonts.otf.FontReadingException;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class GsubHelper {
private Map<Integer, List<Integer>> rawSubstitutionMap;
public GsubHelper(TrueTypeFont font) {
// get tables offsets from the font instance
Map<String, int[]> tables = font.tables;
if (tables.get("GSUB") != null) {
HackedGlyphSubstitutionTableReader gsubReader;
try {
gsubReader = new HackedGlyphSubstitutionTableReader(
font.rf, tables.get("GSUB")[0], glyphToCharacterMap, font.glyphWidthsByIndex);
gsubReader.read();
} catch (IOException | FontReadingException e) {
throw new IllegalStateException(e.getMessage());
}
rawSubstitutionMap = gsubReader.getRawSubstitutionMap();
}
}
/** Returns a glyphId substitution map
*/
public Map<Integer, List<Integer>> getRawSubstitutionMap() {
return rawSubstitutionMap;
}
}
Было бы лучше расширить TrueTypeFont
, но это не сработает с factory методами createFont()
of BaseFont
, которые при создании шрифта будут использовать имена жестких кодированных классов.