在iText中访问OpenType字形变体

时间:2015-11-17 02:36:22

标签: java pdf itext opentype

在iText中使用OpenType字体构建PDF文档时,我想从字体中访问字形变体 - 特别是表格图。由于OpenType字形变体没有Unicode索引,我不知道如何指定我想使用一组特定的变体(表格图)或通过其字形ID调用特定的字形。只需查找相关的iText类名称即可。

1 个答案:

答案 0 :(得分:8)

在最新的标记5.5.8和iText的master分支中似乎都不可能。

正如this article和Microsoft OpenType font file specification中所述,字形变体存储在字体文件的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>> rawLigatureSubstitutionMapOpenTypeFontTableReader当前放弃了要素的符号名称。 rawLigatureSubstitutionMapglyphId变体映射到基础glyphId,或将绑定glyphId映射到glyphIds的序列,如下所示:

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

可以反转此映射以获取基础glyphId的所有变体。因此,所有具有未知unicode值的扩展字形都可以通过它们与基本字形或一系列字形的连接来计算出来。

接下来,为了能够将字形写入PDF,我们需要知道glyphId的unicode值。关系unicode -> glyphIdcmap31中的TrueTypeFont字段映射。反转地图可以通过glyphId获得unicode。

<强>扭捏

rawLigatureSubstitutionMap无法在GlyphSubstitutionTableReader中访问,因为它是private成员,并且没有getter访问者。最简单的方法是复制粘贴原始类并为地图添加一个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,它在创建字体时依赖于硬编码的类名。