前言

Qt在Linux绘制字体是使用的FreeType。

FreeType是一个用C语言实现的一个字体光栅化库。它可以用来将字符栅格化并映射成位图以及提供其他字体相关业务的支持。
FreeType并不提供API以运行更高级的功能,如文字布局或图形处理(例如彩色文本渲染、“空洞化”等)。然而,它提供了一个简单、易用并统一的接口来访问字体文件的内容,从而极大地简化了这些任务。它支持各种字体格式,包括TrueType、Type 1、以及OpenType。
FreeType在两个自由软件许可证的许可下发布:GNU通用公共许可证或者以及一个类BSD许可证。因此这个库能够使用于任何类型的项目中,无论其是否是专有软件。同时也包括正在使用的主要自由桌面系统软件。

流程很简单,Qt在绘制字体时,首先将字体中的描述点以及字形信息保存到QPainterPath中。然后再绘制出来。

下面的代码是我从Qt中扣出来了,这是Qt具体描点到Path的过程,Qt在绘制大号字体的时候会调用下面的逻辑

下面上代码

代码

pro文件中加


LIBS += -lfreetype INCLUDEPATH += /usr/include/freetype2

头文件

//code
#include <freetype2/ft2build.h>
#include <freetype2/freetype/freetype.h>
#include <freetype/freetype.h>
#include <freetype/ftoutln.h>


static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale, QVector<QPoint>& vectors) { x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM); y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM); FT_Vector *p = g->outline.points; const FT_Vector *e = p + g->outline.n_points; while (p < e) { p->x = FT_MulFix(p->x, x_scale); p->y = FT_MulFix(p->y, y_scale); vectors.push_back(QPoint(p->x, p->y)); ++p; } } // 将字形中的 #define GLYPH2PATH_DEBUG QT_NO_QDEBUG_MACRO // qDebug void addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale) { const qreal factor = 1/64.; QVector<QPoint> vectors; scaleOutline(face, g, x_scale, y_scale, vectors); // int xMin = 0; int xMax = 0; foreach (QPoint point, vectors) { if (point.x() > xMax) xMax = point.x(); if (point.x() < xMin) xMin = point.x(); } int glyphWidth = xMax - xMin; QPointF cp = point.toPointF(); // convert the outline to a painter path int i = 0; for (int j = 0; j < g->outline.n_contours; ++j) { int last_point = g->outline.contours[j]; GLYPH2PATH_DEBUG() << "contour:" << i << "to" << last_point; QPointF start = QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); if (!(g->outline.tags[i] & 1)) { // start point is not on curve: if (!(g->outline.tags[last_point] & 1)) { // end point is not on curve: GLYPH2PATH_DEBUG() << " start and end point are not on curve"; start = (QPointF(g->outline.points[last_point].x*factor, -g->outline.points[last_point].y*factor) + start) / 2.0; } else { GLYPH2PATH_DEBUG() << " end point is on curve, start is not"; start = QPointF(g->outline.points[last_point].x*factor, -g->outline.points[last_point].y*factor); } --i; // to use original start point as control point below } start += cp; GLYPH2PATH_DEBUG() << " start at" << start; path->moveTo(start); QPointF c[4]; c[0] = start; int n = 1; while (i < last_point) { ++i; c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); GLYPH2PATH_DEBUG() << " " << i << c[n] << "tag =" << (int)g->outline.tags[i] << ": on curve =" << (bool)(g->outline.tags[i] & 1); ++n; switch (g->outline.tags[i] & 3) { case 2: // cubic bezier element if (n < 4) continue; c[3] = (c[3] + c[2])/2; --i; break; case 0: // quadratic bezier element if (n < 3) continue; c[3] = (c[1] + c[2])/2; c[2] = (2*c[1] + c[3])/3; c[1] = (2*c[1] + c[0])/3; --i; break; case 1: case 3: if (n == 2) { GLYPH2PATH_DEBUG() << " lineTo" << c[1]; path->lineTo(c[1]); c[0] = c[1]; n = 1; continue; } else if (n == 3) { c[3] = c[2]; c[2] = (2*c[1] + c[3])/3; c[1] = (2*c[1] + c[0])/3; } break; } GLYPH2PATH_DEBUG() << " cubicTo" << c[1] << c[2] << c[3]; path->cubicTo(c[1], c[2], c[3]); c[0] = c[3]; n = 1; } if (n == 1) { GLYPH2PATH_DEBUG() << " closeSubpath"; path->closeSubpath(); } else { c[3] = start; if (n == 2) { c[2] = (2*c[1] + c[3])/3; c[1] = (2*c[1] + c[0])/3; } GLYPH2PATH_DEBUG() << " close cubicTo" << c[1] << c[2] << c[3]; path->cubicTo(c[1], c[2], c[3]); } ++i; } } /////////////////// //这里是调用freetype相关的函数 QPainterPath path; FT_Library library; FT_Face face; FT_Error error = FT_Init_FreeType( &library ); QString strFileName = QString::fromUtf8("/home/zhangpf/freeType/FZXBSJW_0.TTF"); std::string strFile = strFileName.toStdString(); const char* fileName = strFile.c_str(); error = FT_New_Face(library, fileName, 0, &face ); uint ch = 'h'; FT_UInt idxGlyph = FT_Get_Char_Index(face, ch); FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0); // 这是斜体 // FT_Matrix matrix; // double angle = ( 25.0 / 360 ) * 3.14159 * 2; // 180/12=15 度 // matrix.xx = (FT_Fixed)(cos(angle) * 0x10000); // matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000); // matrix.yx = (FT_Fixed)(sin(angle) * 0x10000); // matrix.yy = (FT_Fixed)(cos(angle) * 0x10000); // FT_Set_Transform(face, &matrix, 0); // 设置或重置旋转 FT_Error err = FT_Load_Glyph (face, idxGlyph, FT_LOAD_NO_BITMAP); FT_BBox bbox = { 0 }; FT_Outline_Get_CBox(&face->glyph->outline, &bbox); /* only oblique outline glyphs */ if ( face->glyph->format != FT_GLYPH_FORMAT_OUTLINE ) return; // 用这个也可以缩放一半 // FT_Outline* outline = &(face->glyph->outline); // FT_Matrix transform; // transform.xx = 0x10000L * 0.5; // transform.yx = 0x00000L; // transform.xy = 0x00000L; // transform.yy = 0x10000L; // FT_Outline_Transform( outline, &transform ); QFixedPoint p; p.x = 0; p.y = 0; if (!FT_IS_SCALABLE(face)) { qDebug()<<"123"<<endl; } else addGlyphToPath(face, face->glyph, p, &path, (face->units_per_EM << 6) / 2, face->units_per_EM << 6); //宽度缩放一半

最后在paintevent中可以绘制出来,由于字形的point中有负数,painter需要transform一下


QPainter painter(this); QTransform transform; transform.translate(500,500); painter.setTransform(transform); painter.drawPath(path);

这是Qt绘制文字的一部分逻辑,我扣出来Demo来给大家学习。主要是使用了QPainterPath来描述字体中的点,以及保存成字体中的贝塞尔曲线。生成QPainterPath来描述具体的字形。代码中我还添加了缩放的变形,实际上字体的绘制变形有很多方法。就拿缩放来讲,就有这么几种


// 用这个也可以缩放一半 FT_Outline* outline = &(face->glyph->outline); FT_Matrix transform; transform.xx = 0x10000L * 0.5; transform.yx = 0x00000L; transform.xy = 0x00000L; transform.yy = 0x10000L; FT_Outline_Transform( outline, &transform ); // 这是斜体实现,matrix.xx * 0.5 可以缩放 0.5倍 FT_Matrix matrix; double angle = ( 25.0 / 360 ) * 3.14159 * 2; // 25 度 matrix.xx = (FT_Fixed)(cos(angle) * 0x10000); matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000); matrix.yx = (FT_Fixed)(sin(angle) * 0x10000); matrix.yy = (FT_Fixed)(cos(angle) * 0x10000); FT_Set_Transform(face, &matrix, 0); // 设置或重置旋转 //甚至可以在 点->path这一步来做 (face->units_per_EM << 6) / 2, //addGlyphToPath(face, face->glyph, p, &path, (face->units_per_EM << 6) / 2, face->units_per_EM << 6); //宽度缩放一半 (face->units_per_EM << 6) / 2,

所以大部分UI库都有自己的实现方法。这也是freetype灵活的地方.