forked from openkylin/rust-ttf-parser
161 lines
3.8 KiB
C++
161 lines
3.8 KiB
C++
#include <QTransform>
|
|
#include <QDebug>
|
|
|
|
#include <hb.h>
|
|
|
|
#include "harfbuzzfont.h"
|
|
|
|
struct Outliner
|
|
{
|
|
static void moveToFn(hb_position_t to_x, hb_position_t to_y, Outliner &outliner)
|
|
{
|
|
outliner.path.moveTo(to_x, to_y);
|
|
}
|
|
|
|
static void lineToFn(hb_position_t to_x, hb_position_t to_y, Outliner &outliner)
|
|
{
|
|
outliner.path.lineTo(to_x, to_y);
|
|
}
|
|
|
|
static void quadToFn(hb_position_t control_x, hb_position_t control_y,
|
|
hb_position_t to_x, hb_position_t to_y,
|
|
Outliner &outliner)
|
|
{
|
|
outliner.path.quadTo(control_x, control_y, to_x, to_y);
|
|
}
|
|
|
|
static void cubicToFn(hb_position_t control1_x, hb_position_t control1_y,
|
|
hb_position_t control2_x, hb_position_t control2_y,
|
|
hb_position_t to_x, hb_position_t to_y,
|
|
Outliner &outliner)
|
|
{
|
|
outliner.path.cubicTo(control1_x, control1_y, control2_x, control2_y, to_x, to_y);
|
|
}
|
|
|
|
static void closePathFn(Outliner &outliner)
|
|
{
|
|
outliner.path.closeSubpath();
|
|
}
|
|
|
|
QPainterPath path;
|
|
};
|
|
|
|
HarfBuzzFont::HarfBuzzFont()
|
|
{
|
|
|
|
}
|
|
|
|
HarfBuzzFont::~HarfBuzzFont()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
void HarfBuzzFont::open(const QString &path, const quint32 index)
|
|
{
|
|
if (isOpen()) {
|
|
reset();
|
|
}
|
|
|
|
const auto utf8Path = path.toUtf8();
|
|
hb_blob_t *blob = hb_blob_create_from_file(utf8Path.constData());
|
|
if (!blob) {
|
|
throw tr("Failed to open a font.");
|
|
}
|
|
|
|
hb_face_t *face = hb_face_create(blob, index);
|
|
if (!face) {
|
|
throw tr("Failed to open a font.");
|
|
}
|
|
|
|
hb_font_t *font = hb_font_create(face);
|
|
if (!font) {
|
|
throw tr("Failed to open a font.");
|
|
}
|
|
|
|
m_blob = blob;
|
|
m_face = face;
|
|
m_font = font;
|
|
}
|
|
|
|
bool HarfBuzzFont::isOpen() const
|
|
{
|
|
return m_font != nullptr;
|
|
}
|
|
|
|
Glyph HarfBuzzFont::outline(const quint16 gid) const
|
|
{
|
|
if (!isOpen()) {
|
|
throw tr("Font is not loaded.");
|
|
}
|
|
|
|
Outliner outliner;
|
|
|
|
hb_draw_funcs_t *funcs = hb_draw_funcs_create();
|
|
hb_draw_funcs_set_move_to_func(funcs, (hb_draw_move_to_func_t)outliner.moveToFn);
|
|
hb_draw_funcs_set_line_to_func(funcs, (hb_draw_line_to_func_t)outliner.lineToFn);
|
|
hb_draw_funcs_set_quadratic_to_func(funcs, (hb_draw_quadratic_to_func_t)outliner.quadToFn);
|
|
hb_draw_funcs_set_cubic_to_func(funcs, (hb_draw_cubic_to_func_t)outliner.cubicToFn);
|
|
hb_draw_funcs_set_close_path_func(funcs, (hb_draw_close_path_func_t)outliner.closePathFn);
|
|
|
|
if (!hb_font_draw_glyph(m_font, gid, funcs, &outliner)) {
|
|
throw tr("Failed to outline a glyph %1.").arg(gid);
|
|
}
|
|
|
|
hb_draw_funcs_destroy(funcs);
|
|
|
|
hb_glyph_extents_t extents = {0, 0, 0, 0};
|
|
if (!hb_font_get_glyph_extents(m_font, gid, &extents)) {
|
|
throw tr("Failed to query glyph extents.");
|
|
}
|
|
|
|
const QRect bbox(
|
|
extents.x_bearing,
|
|
-extents.y_bearing,
|
|
extents.width,
|
|
-extents.height
|
|
);
|
|
|
|
// Flip outline around x-axis.
|
|
QTransform ts(1, 0, 0, -1, 0, 0);
|
|
outliner.path = ts.map(outliner.path);
|
|
|
|
outliner.path.setFillRule(Qt::WindingFill);
|
|
|
|
return Glyph {
|
|
outliner.path,
|
|
bbox,
|
|
};
|
|
}
|
|
|
|
void HarfBuzzFont::setVariations(const QVector<Variation> &variations)
|
|
{
|
|
if (!isOpen()) {
|
|
throw tr("Font is not loaded.");
|
|
}
|
|
|
|
QVector<hb_variation_t> hbVariations;
|
|
for (const auto &var : variations) {
|
|
hbVariations.append({ var.tag.value, (float)var.value });
|
|
}
|
|
|
|
hb_font_set_variations(m_font, hbVariations.constData(), hbVariations.size());
|
|
}
|
|
|
|
void HarfBuzzFont::reset()
|
|
{
|
|
if (m_blob) {
|
|
hb_blob_destroy(m_blob);
|
|
m_blob = nullptr;
|
|
}
|
|
|
|
if (m_font) {
|
|
hb_font_destroy(m_font);
|
|
m_font = nullptr;
|
|
}
|
|
|
|
if (m_face) {
|
|
hb_face_destroy(m_face);
|
|
m_face = nullptr;
|
|
}
|
|
}
|