Подтвердить что ты не робот

Доступ к вариантам глифов OpenType в iText

При создании PDF-документов с использованием шрифтов OpenType в iText я хочу получить доступ к вариантам глифов из шрифта - в частности, табличные данные. Поскольку варианты глифов OpenType не имеют индексов Unicode, я не уверен, как указать, что я хочу использовать определенный набор вариантов (табличные данные), или вызвать определенный глиф по его идентификатору глифа. Просто найдите соответствующее имя класса iText, если оно существует.

4b9b3361

Ответ 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, которые при создании шрифта будут использовать имена жестких кодированных классов.