Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get font outlines programmatically

Tags:

java

android

Is it somehow possible to retrieve font (ttf/otf) outline as a curve/series of points on Android? For example, if I wanted to convert a word with specific font into a vector format?

like image 385
Axarydax Avatar asked Oct 25 '14 13:10

Axarydax


1 Answers

Since I never developed for an Android device, I will give you the way to do that, but in Java.

This is a couple of good libraries, but I don't know if it would be possible for you to use it (C/C++) So I will explain you how to do it yourself.

You should in first convert your word in a shape using a TextLayout (an immutable graphical representation of styled character data that you can draw) in a FontRenderContext.

According to the John J Smith answer here: https://stackoverflow.com/a/6864113/837765, It should be possible to use something similar to a TextLayout on Android. But, there's no quivalent of FontRenderContext. As I said, I never developed for an Android device, but there is probably (I hope so) a workaround to convert characters in a shape.

In Java Something like this should work (to convert text in a Shape):

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;
}

Then, you should find the shape boundary. It's not pretty difficult here, because your shape can give you directly the path iterator with shape.getPathIterator(null)

On each iteration, you can get the current segment, its type and the coordinates.:

  1. SEG_QUADTO: a quadratic parametric curve;
  2. SEG_CUBICTO : a cubic parametric curve;
  3. SEG_LINETO : specifies the end point of a line;
  4. SEG_MOVETO : a point that specifies the starting location for a new subpath.

At this point, you should read about Bézier curve here and here.

You will learn that:

Any quadratic spline can be expressed as a cubic (where the cubic term is zero). The end points of the cubic will be the same as the quadratic's.

CP0 = QP0 CP3 = QP2

The two control points for the cubic are:

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

So converting from TrueType to PostScript is trivial.

In Java Something like this should work:

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;
}

I created a demo project on Bitbucket, maybe it could help you. https://bitbucket.org/pieralexandre/fontshape

Initial shape text (after outline transformation):

enter image description here

The points on the shape:

enter image description here

Only the points:

enter image description here

And the output of all points:

(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)
//...

I know you cannot use Graphics2D (I used it for the UI) in Android, but you should be able to use my solution for an Android Project (I hope).

After that, you could be able (with the help of Bézier curve) to recreate your curves.

Also, there's a couple of other good tools here. take a look at this one:

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

It's in Javascript, but maybe it could help you even more.

like image 64
Pier-Alexandre Bouchard Avatar answered Nov 11 '22 00:11

Pier-Alexandre Bouchard