#include #include #include #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 &variations) { if (!isOpen()) { throw tr("Font is not loaded."); } QVector 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; } }