一尘不染

在iText中访问OpenType字形变体

java

在iText中使用OpenType字体构建PDF文档时,我想从字体内访问字形变体-
特别是表格图形。由于OpenType字形变体没有Unicode索引,因此我不确定如何指定我要使用一组特定的变体(表格形式)还是通过其字形ID调用特定字形。如果存在,只需寻找相关的iText类名称。


阅读 371

收藏
2020-12-03

共1个答案

一尘不染

在最新的标记5.5.8和iText
master分支中似乎都无法做到这一点。

本文和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映射将一个glyphId变体映射到base
glyphId,或者将一个连字映射glyphId到这样的序列glyphIds

629 -> 66 // a.feature -> a
715 -> 71, 71, 77 // ffl ligature

可以颠倒此映射关系以获得base的所有变体glyphId。因此,具有未知unicode值的所有扩展字形都可以通过与基本字形或一系列字形的连接来找出。

接下来,要能够将字形写入PDF,我们需要知道该字符的unicode值glyphId。关系unicode -> glyphId由中的cmap31字段映射TrueTypeFont。反向映射通过glyphId给出unicode。

调整

rawLigatureSubstitutionMap无法在中进行访问GlyphSubstitutionTableReader,因为它是private成员,并且没有getter访问器。最简单的技巧是复制粘贴原始类并为地图添加吸气剂:

public class HackedGlyphSubstitutionTableReader extends OpenTypeFontTableReader {

    // copy-pasted code ...

    public Map<Integer, List<Integer>> getRawSubstitutionMap() {
        return rawLigatureSubstitutionMap;
    }
}

接下来的问题是,GlyphSubstitutionTableReader需要对偏移GSUB表,被存储在信息protected HashMap<String, int[]> tablesTrueTypeFont类。放在同一包中的帮助程序类将桥接对的受保护成员的访问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,但不会与工厂方法工作createFont()BaseFont,创建一种字体时依赖于硬编码的类名称。

2020-12-03