如何使用从TTF文件提取的字距调整对在Java中将字形正确显示为Path2D?

时间:2020-11-10 00:14:43

标签: java true-type-fonts glyph kerning java-font

该问题与在 Java 中恢复字形字体信息有关,它与question posted here.有关。有关更多详细信息,请检查问题和解答。

由于Java不提供此信息,因此建议使用Apache FOP库直接从Truetype文件中恢复字距对。然后,我将库移植到Windows并使用以下代码恢复字距调整对:

TTFFile file;
File ttf = new File("C:\\Windows\\Fonts\\calibri.ttf" );
try { file = TTFFile.open(ttf); }
catch (IOException e) {e.printStackTrace(); }
Map<Integer, Map<Integer, Integer>> kerning = file.getKerning();

最后,库起作用,但是紧缩对返回不起作用,并且使用该功能在 Path2D.Float 中检索到的字形。下面以及紧随其后的代码片段:

void vectorize(Path2D.Float path, String s) {
    PathIterator pIter;
    FontRenderContext frc = new FontRenderContext(null,true,true);
    GlyphVector gv;
    Shape glyph;
    gv = font.createGlyphVector(frc, s);
    glyph = gv.getGlyphOutline(0);
    pIter = glyph.getPathIterator(null);
    while (!pIter.isDone()) {
        switch(pIter.currentSegment(points)) {
        case PathIterator.SEG_MOVETO:
            path.moveTo(points[0], points[1]);
            break;
        case PathIterator.SEG_LINETO :
            path.lineTo(points[0], points[1]);
            break;
        case PathIterator.SEG_QUADTO :
            path.quadTo(points[0], points[1], points[2], points[3]);
            break;
        case PathIterator.SEG_CUBICTO :
            path.curveTo(points[0], points[1], points[2], points[3], points[4], points[5]);
            break;
        case PathIterator.SEG_CLOSE :
            path.closePath();
        }
        pIter.next();
    } 
}

字形长度被检索到数组 lens

Font font = new Font("Calibri", Font.PLAIN, 1000);
double interchar = 1000. * 0.075;
int size = '}' - ' ' + 1;
Path2D.Float[] glyphs = new Path2D.Float[size];
double[] lens = new double[size];
String chars[] = new String[size];
int i; char c; 
char[] s = { '0' };
for (i = 0, c = ' '; c <= '}'; c++, i++) { s[0] = c; chars[i] = new String(s); }
for (i = 0; i < size; i++) {
    vectorize(glyphs[i] = new Path2D.Float(), chars[i]); // function shown above
    lens[i] = glyphs[i].getBounds2D().getWidth() + interchar;
}

请清楚一点,我使用Graphics2D中的 fill 显示字形,并按照建议的方式使用上述长度添加到Apache FOP库返回的字距置换中来翻译字形,但结果令人震惊。正如该讨论中所建议的,字体大小是标准的1000,而 interchar 结果是75。所有这些似乎都是正确的,但我的手动字距调整对看起来比使用字距调整好得多对从TTF文件。

该库或Truetype字体中是否有任何知识丰富的人士能够告诉我们我们应该如何使用这些字距调整对?

是否需要直接从TTF文件访问字形,而不是如上所述使用Java字体管理?如果是,请如何?

2 个答案:

答案 0 :(得分:0)

GNU Classpath包含一个示例 gnu.classpath.examples.awt。 HintingDemo.java ,它可能有助于解决此问题。本示例使您可以可视化字形。它读取字体并解释语言以获取其中给出的提示。您可以选择显示带提示或不带提示(带提示的字形对于较小的字体很有用,但不建议在大字体中使用)。如果您不习惯Truetype提示,则通过此演示您将了解它们在整数边界内对齐路径。该程序不是很花哨,但是它具有读取字形和解释提示的所有必要工具,其优点是可视化结果。

您不需要整个程序包即可编译和运行此演示。如果使用的是Eclipse,则很容易为它创建一个项目。首先创建包 gnu.classpath.examples.awt ,然后在其中导入 HintingDemo.java 。然后,您只需一次导入一个文件或整个包的所有依赖项。例如,您可以导入整个软件包 gnu.java.awt.font 并擦除 OpenTypeFontPeer.java (演示程序不需要它,如果您这样做,则会导致错误离开)。

这提供了一种直接从字体文件读取和显示字形的独立方法。有趣的是,它不使用任何字距调整信息。这必须与Apache FOP库一起添加。如果两次读取文件是一个问题,则需要一种解决方法,要么深入GNU Classpath以获取相同的信息,要么尝试使Apache FOP与GNU Classpath进行“交谈”。目前,我无法说这有多难。我仅将其用作复制信息并在其他地方使用的工具,而不是在实际程序中真正读取字体文件的方式。字体非常紧凑,但不是显示文本的最有效方法,尤其是在解释字体语言的情况下,例如Type 1和Truetype字体。如果您愿意追求高质量和高速度,那么摆脱这种解释似乎是个好主意。

答案 1 :(得分:0)

问题解决了!

回想一下,使用库Apache FOP,需要打开该文件并获取字距对,这需要使用以下代码:

TTFFile file;
File ttf = new File("C:\\Windows\\Fonts\\calibri.ttf" );
try { file = TTFFile.open(ttf); }
catch (IOException e) {e.printStackTrace(); }
Map<Integer, Map<Integer, Integer>> kerning = file.getKerning();

下面用于向量化字形的代码现在是正确的:

Font font = new Font("Calibri", Font.PLAIN, 2048);
int size = '}' - ' ' + 1;
Path2D.Float[] glyphs = new Path2D.Float[size];
//double[] lens = new double[size];
String chars[] = new String[size];
int i; char c; 
char[] s = { '0' };
for (i = 0, c = ' '; c <= '}'; c++, i++) { s[0] = c; chars[i] = new String(s); }
for (i = 0; i < size; i++) {
    vectorize(glyphs[i] = new Path2D.Float(), chars[i]);
    //lens[i] = glyphs[i].getBounds2D().getWidth();
}

请注意,现在的字体大小为2048,这是此特定字体的unitsPerEm。如here所述,此值由字体文件中的HEAD标记提供。

请注意,数组lens和上面的代码注释不出宽度。必须从文件中读取它。使用Apache FOP中的int width = getCharWidthRaw(prev),其中prev是前一个字符,width是文件中写入的字符的原始宽度。该值必须添加到可在映射kerning中获得的字距对值。

该映射以这种方式使用:kerning.get(prev)返回另一个包含要添加的字符和字距值的映射。如果在此映射中找到了下一个要显示的字符,则将相应的值添加到width中。如果找不到,或者返回null,则此对没有字距调整值。

这是显示字距调整现在有效的文本。

Text with correct kerning and correct character widths