最近在Android平台上生成PDF文件时,碰到了内嵌字体的问题。大部分打印厂的机器是不支持PDF内嵌字体的,
所以要想将文字按指定的字体印刷出来,只能转曲

转曲

转曲的意思就是把文字转成曲线线段,可以想象成把文字的轮廓勾勒出来。
Android中的Paint.getTextPath
方法可以获得String的Path,而PathMeasure可以获得Path中的关键点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextSize(yourTextSize);
Path fontPath = new Path();
paint.getTextPath(yourText, 0, yourText.length(), 0f, paint.getFontSpacing(), fontPath);
PathMeasure pathMeasure = new PathMeasure(fontPath, false);
float[] pos = new float[2]; // pos用来存放每一个关键点
do {
float distance = 0f;
while (distance < pathMeasure.getLength()) {
pathMeasure.getPosTan(distance, pos, null);
// 在这里将pos设置到你自己的曲线里
// 我使用PDFjet生成PDF,那么这里就将pos保存到com.pdfjet.Point中
distance += 0.1f; // 0.1f是每个关键点的间隔。间隔越大轮廓越粗糙,反之越细腻。如果设的过小会影响性能。
}
} while (pathMeasure.nextContour());// nextContour会跳到下一段曲线

计算曲线嵌套层数

得到文字曲线后还不能直接印刷,因为我们只是得到了文字的轮廓,还得往轮廓里填色。
当然不能所有轮廓都填黑色,这样文字会变成一坨一坨的黑色……
我们需要计算每条曲线被多少条曲线全包围(注意我们现在得到的曲线全都是头尾相连的)。
如果被偶数条曲线(包含0条)包围,则填黑色;如果被奇数条曲线包围,则填白色(底色)。
Android或Java本身不提供这样的方法,还好已经有人帮我们研究出方法了,详细参看http://alienryderflex.com/polygon/。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static class Polygon {
// Polygon coodinates.
private float[] polyY, polyX;

// Number of sides in the polygon.
private int polySides;

/**
* Default constructor.
* @param px Polygon y coods.
* @param py Polygon x coods.
* @param ps Polygon sides count.
*/
public Polygon( float[] px, float[] py, int ps ) {
polyX = px;
polyY = py;
polySides = ps;
}

/**
* Checks if the Polygon contains a point.
* @see "http://alienryderflex.com/polygon/"
* @param x Point horizontal pos.
* @param y Point vertical pos.
* @return Point is in Poly flag.
*/
public boolean contains( float x, float y ) {
boolean oddTransitions = false;
for( int i = 0, j = polySides -1; i < polySides; j = i++ ) {
if((Float.compare(polyY[i], y) < 0 && Float.compare(polyY[j], y) >= 0 ) || ( Float.compare(polyY[j], y) < 0 && Float.compare(polyY[i], y) >= 0)){
if(Float.compare(polyX[i] + ( y - polyY[i] ) / ( polyY[j] - polyY[i] ) * ( polyX[j] - polyX[i] ), x) < 0 ) {
oddTransitions = !oddTransitions;
}
}
}
return oddTransitions;
}
}

Polygon的构造方法前两个参数填多边形(可以将曲线理解为多边形)横坐标和纵坐标的数组,第三个参数填多边形的边数,即坐标的个数。
contains方法即可检查某个点是否在这个多边形中。
那么思路就很简单了:如果曲线A的任一关键点不在曲线B中,B就没有包围A。
代码就不贴了。

填色

如上所述,如果被偶数条曲线包围,填黑色;被奇数条曲线包围,填白色。

1
2
3
4
5
6
7
int nestingNum = surroundedNumber(pathPointsList, pathPoints);
if (nestingNum % 2 == 0) {
paint.setColor(Color.BLACK);
} else {
paint.setColor(Color.WHITE);
}
canvas.drawPath(path, paint);

大功告成~

详细代码请参考https://gist.github.com/qq157755587/32e927e77e259cd84631