以编程方式获取字体轮廓

8

在安卓系统上,是否有一种方法可以将字体(ttf/otf)轮廓作为曲线/点系列检索出来?例如,如果我想将使用特定字体的单词转换为矢量格式。

1个回答

8

由于我从未为Android设备开发过,所以我将向您介绍如何使用Java进行开发。

有几个好的库,但我不知道是否可以使用它(C/C++)。因此,我将向您解释如何自己完成。

首先,您应该使用TextLayout(一种不可变的图形化表示样式化字符数据的方法,可以在FontRenderContext中绘制)将单词转换为形状。

根据John J Smith在这里的回答:https://dev59.com/clnUa4cB1Zd3GeqPb5hh#6864113,应该可以在Android上使用类似于TextLayout的东西。但是,没有相应的FontRenderContext。正如我所说,我从未为Android设备开发过,但可能(我希望如此)有一种解决方案可以将字符转换为形状。

在Java中,可以尝试以下代码(将文本转换为形状):

public Shape getShape(String text, Font font, Point from) {
    FontRenderContext context = new FontRenderContext(null, false, false);

    GeneralPath shape = new GeneralPath();
    TextLayout layout = new TextLayout(text, font, context);

    Shape outline = layout.getOutline(null);
    shape.append(outline, true);

    return shape;
}

然后,您应该找到形状边界。这里并不太难,因为您的形状可以直接使用shape.getPathIterator(null)提供路径迭代器。
在每次迭代中,您可以获取当前段、其类型和坐标:
  1. SEG_QUADTO: 一个二次参数曲线;
  2. SEG_CUBICTO : 一个三次参数曲线;
  3. SEG_LINETO : 指定线的终点;
  4. SEG_MOVETO : 指定新子路径的起始位置。
此时,您应该阅读有关Bézier曲线的这里这里的内容。
您将了解到:

任何二次样条都可以表示为三次样条(其中三次项为零)。立方体的端点将与二次体的端点相同。

CP0 = QP0 CP3 = QP2

该立方体的两个控制点是:

CP1 = QP0 + 2/3 *(QP1-QP0) CP2 = CP1 + 1/3 *(QP2-QP0)

因此,从TrueType转换为PostScript很容易。
在Java中,应该可以像这样工作:
public List<Point> getPoints(Shape shape) {
    List<Point> out = new ArrayList<Point>();
    PathIterator iterator = shape.getPathIterator(null);

    double[] coordinates = new double[6];
    double x = 0, y = 0;

    while (!iterator.isDone()) {

        double x1 = coordinates[0];
        double y1 = coordinates[1];

        double x2 = coordinates[2];
        double y2 = coordinates[3];

        double x3 = coordinates[4];
        double y3 = coordinates[5];

        switch (iterator.currentSegment(coordinates)) {
        case PathIterator.SEG_QUADTO:
            x3 = x2;
            y3 = y2;

            x2 = x1 + 1 / 3f * (x2 - x1);
            y2 = y1 + 1 / 3f * (y2 - y1);

            x1 = x + 2 / 3f * (x1 - x);
            y1 = y + 2 / 3f * (y1 - y);

            out.add(new Point(x3, y3));

            x = x3;
            y = y3;
            break;

        case PathIterator.SEG_CUBICTO:
            out.add(new Point(x3, y3));
            x = x3;
            y = y3;
            break;
        case PathIterator.SEG_LINETO:
            out.add(new Point(x1, y1));
            x = x1;
            y = y1;
            break;
        case PathIterator.SEG_MOVETO:
            out.add(new Point(x1, y1));
            x = x1;
            y = y1;
            break;
        }
        iterator.next();
    }

    return out;
}

我在Bitbucket上创建了一个演示项目,也许可以帮助你。 https://bitbucket.org/pieralexandre/fontshape 初始形状文本(轮廓转换后): enter image description here 形状上的点: enter image description here 仅点: enter image description here 所有点的输出:
(0.0,0.0)
(9.326171875,200.0)
(9.326171875,127.734375)
(0.0,0.0)
(50.7080078125,130.126953125)
(62.158203125,138.232421875)
(69.82421875,162.158203125)
(60.302734375,190.087890625)
(50.78125,200.0)
//...

我知道您不能在Android中使用Graphics2D(我在UI中使用了它),但是您应该能够将我的解决方案用于Android项目(我希望如此)。

之后,您可以通过Bézier曲线的帮助重新创建曲线。

此外,这里还有一些其他好用的工具。看看这个:

http://nodebox.github.io/opentype.js/

这是一个Javascript工具,也许它甚至可以更好地帮助您。


根据Romain Guys在这里的回答https://dev59.com/clnUa4cB1Zd3GeqPb5hh,Android没有TextLayout和FontRenderContext的等效物。 - Ridcully
1
被接受的答案提供了一个TextLayout的解决方案...如果我的解决方案无法移植到Android,我会将其删除。它能够移植到Android吗? - Pier-Alexandre Bouchard
我不知道是否可以移植。我猜可能不行。Romain Guy是Android的主要开发人员之一,所以我盲目地相信他对此的所有说法 :-) - Ridcully

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接