【问题标题】:Accessing OpenType glyph variants in iText在 iText 中访问 OpenType 字形变体
【发布时间】:2016-02-18 07:22:36
【问题描述】:

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

【问题讨论】:

标签: java pdf itext opentype


【解决方案1】:

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

正如this article 和Microsoft 的OpenType font file specification 中所述,字形变体存储在字体文件的Glyph Substitution Table (GSUB) 中。访问字形变体需要从文件中读取这个表,这实际上是在 com.itextpdf.text.pdf.fonts.otf.GlyphSubstitutionTableReader 类中实现的,尽管这个类目前被禁用。

com.itextpdf.text.pdf.TrueTypeFontUnicode 类中的调用 readGsubTable() 被注释掉。

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 变体映射到一个碱基glyphId,或者将一个连字glyphId 映射到一个glyphIds 序列,如下所示:

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

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

接下来,为了能够将字形写入 PDF,我们需要知道 glyphId 的 unicode 值。关系unicode -> glyphIdTrueTypeFont 中的cmap31 字段映射。反转地图通过 glyphId 提供 unicode。

调整

rawLigatureSubstitutionMap 不能在GlyphSubstitutionTableReader 中访问,因为它是private 成员并且没有getter 访问器。最简单的 hack 是复制粘贴原始类并为地图添加一个 getter:

public class HackedGlyphSubstitutionTableReader extends OpenTypeFontTableReader {

    // copy-pasted code ...

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

下一个问题是GlyphSubstitutionTableReader 需要GSUB 表的偏移量,该信息存储在protected HashMap&lt;String, int[]&gt; 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 会更好,但这不适用于BaseFont 的工厂方法createFont(),它在创建字体时依赖于硬编码的类名。

【讨论】:

  • 你说话和引用权威;然而,我很难相信用 Java 解析 GSUB 表是不可能的。您的意思是说即使您可以解析 GSUB 表,iText 也不包含使用“原始”字形索引的基础架构,因此作为添加 GSUB 替换的一部分,当前文本处理iText 的范式是否需要全部替换?
  • 对不起,我的回答实在是太笼统了,甚至是愚蠢的。它没有反映全部的真相。我的意思是在当前版本的 iText 中使用字形替换的 API 尚未完成。但基础设施主要是到位。它可以与 iText 类的一些复制粘贴和调整一起使用。我将用更多细节更新我的答案。
  • 我认为GSUB的低级别是可以的。数据的每一部分似乎都是从文件中解析出来的(虽然没有对此功能进行测试)。但是 API 级别还没有完成,原始数据仍然封装在基础设施类中,而不是通过有意义的用例传递给 API。同时,这个故事中的关键 iText 类不是为扩展而设计的,这使得定制解决方案变得困难。
  • 我认为您混淆了 masterdevelop 分支。 master 分支的尖端始终与最新版本标签相同。 develop 分支包含当前(预发布)开发。我应该知道,因为我是创建标签的人。 :)
  • @AmedeeVanGasse 是的,你是对的,我知道 :) 但是这个故事所涉及的代码部分并不重要。自从 7-10 个月前,在最新的发布标签之前很久,它就没有改变过。
猜你喜欢
  • 2011-12-14
  • 2012-12-13
  • 2020-07-29
  • 1970-01-01
  • 2019-06-12
  • 1970-01-01
  • 2013-09-18
  • 2023-03-25
  • 2011-01-04
相关资源
最近更新 更多