在开发图形代码的过程中,需要将一些字体字形转换为SVG路径,以便在代码中合理地绘制它们。经过一番考察,决定编写自己的实用工具,使用U++框架,整个过程仅花费了20分钟。虽然这是一个相当小众的工具,但认为这个工具及其代码描述对更广泛的公众可能很有用。
SVG路径是一种简单的文本格式,使用移动/线/曲线原语描述图形形状。与此同时,当前的字体系统也使用完全相同的原语绘制字形。因此,如果打算编写字体和SVG路径之间的转换,基本上需要从字体中提取特定字形的曲线信息,并将其转换为SVG路径的文本表示。
在U++中,字体由Font
类型表示。Font
提供了一个Render
方法,该方法将代表Unicode代码点ch
的字形渲染到纯虚拟接口FontGlyphConsumer
上:
void Render(FontGlyphConsumer& sw, double x, double y, int ch) const;
现在,显而易见的任务是通过实现这个接口的类来生成SVG路径输出:
struct TextToSvg : FontGlyphConsumer {
String t; // 这里累积SVG路径文本
void Put(Pointf p); // 将点作为文本坐标放入SVG路径文本
virtual void Move(Pointf p);
virtual void Line(Pointf p);
virtual void Quadratic(Pointf p1, Pointf p2);
virtual void Cubic(Pointf p1, Pointf p2, Pointf p3);
virtual void Close();
};
这个类定义了如何将字体的绘制命令转换为SVG路径命令。例如,Move
方法会生成一个'M'命令,Line
方法会生成一个'L'命令,依此类推。
为了完善这个工具,需要添加一个GUI对话框。它将允许用户更改字体和文本,在只读编辑器区域显示SVG路径,并允许将SVG路径复制到剪贴板。同时,还将提供渲染路径的预览。
从设计U++布局设计器中的对话框布局开始:
注意,保持预览字段'未类型化',这意味着稍后将提供该小部件的类型。
将布局文件包含到代码中:
#define LAYOUTFILE
#include
现在让准备预览小部件类:
struct Preview : Ctrl {
String svgpath;
virtual void Paint(Draw& w);
};
显然,这并不复杂,只需要在这里保留生成的SVG路径的副本,并在需要时绘制它:
void Preview::Paint(Draw& w) {
DrawPainter sw(w, GetSize());
sw.Clear(SWhite());
sw.Path(svgpath).Fill(SBlack());
}
在U++中,基本渲染类Draw
相当简单,提供了足够的功能来绘制彩色矩形、文本和图像,可能还有硬件加速。与此同时,有一个更精细的软件渲染器Painter
,它或多或少提供了SVG或PDF渲染所需的所有图形原语。这个渲染器能做的任务之一就是渲染SVG路径,这正是需要的。构造了DrawPainter
,它是Draw
和Painter
之间的桥梁,然后清除背景(SWhite
与普通的White
不同,它是根据最终的暗色主题模式调整颜色的 - 在这种情况下,它实际上是黑色)。渲染路径并用SBlack
颜色填充它(同样,在暗色主题模式下它是白色)。
现在是创建对话框类的时候了,实际上这相当简单:
struct TextToSvgPathDlg : public WithTextToSvgPathLayout {
Preview preview;
void Render();
TextToSvgPathDlg();
};
在这里,将创建的布局添加到TopWindow
类(这代表顶级窗口)并使用结果作为对话框类的基类。这将添加所有小部件作为成员变量,除了那些未类型化的,这正是preview
小部件的情况,所以在这里将其添加为成员变量。在这里定义的唯一方法是Render
,因为这将是对话框对大多数用户操作的反应。
然后大部分工作是在构造函数中完成的:
TextToSvgPathDlg::TextToSvgPathDlg() {
CtrlLayout(*this, "Text to SVG path converter");
// ... 省略中间代码 ...
for(Ctrl *q = GetFirstChild(); q; q = q->GetNext())
if(!dynamic_cast
GUI_APP_MAIN {
TextToSvgPathDlg().Run();
}