#include #include #include #include "ttfparserfont.h" struct Outliner { static void moveToFn(float x, float y, void *user) { auto self = static_cast(user); self->path.moveTo(double(x), double(y)); } static void lineToFn(float x, float y, void *user) { auto self = static_cast(user); self->path.lineTo(double(x), double(y)); } static void quadToFn(float x1, float y1, float x, float y, void *user) { auto self = static_cast(user); self->path.quadTo(double(x1), double(y1), double(x), double(y)); } static void curveToFn(float x1, float y1, float x2, float y2, float x, float y, void *user) { auto self = static_cast(user); self->path.cubicTo(double(x1), double(y1), double(x2), double(y2), double(x), double(y)); } static void closePathFn(void *user) { auto self = static_cast(user); self->path.closeSubpath(); } QPainterPath path; }; TtfParserFont::TtfParserFont() { } void TtfParserFont::open(const QString &path, const quint32 index) { if (isOpen()) { m_face.reset(); } QFile file(path); file.open(QFile::ReadOnly); m_fontData = file.readAll(); m_face.reset((ttfp_face*)malloc(ttfp_face_size_of())); const auto res = ttfp_face_init(m_fontData.constData(), m_fontData.size(), index, m_face.get()); if (!res) { throw tr("Failed to open a font."); } } bool TtfParserFont::isOpen() const { return m_face != nullptr; } FontInfo TtfParserFont::fontInfo() const { if (!isOpen()) { throw tr("Font is not loaded."); } return FontInfo { ttfp_get_ascender(m_face.get()), ttfp_get_height(m_face.get()), ttfp_get_number_of_glyphs(m_face.get()), }; } Glyph TtfParserFont::outline(const quint16 gid) const { if (!isOpen()) { throw tr("Font is not loaded."); } Outliner outliner; ttfp_outline_builder builder; builder.move_to = outliner.moveToFn; builder.line_to = outliner.lineToFn; builder.quad_to = outliner.quadToFn; builder.curve_to = outliner.curveToFn; builder.close_path = outliner.closePathFn; ttfp_rect rawBbox; const bool ok = ttfp_outline_glyph( m_face.get(), builder, &outliner, gid, &rawBbox ); if (!ok) { return Glyph { QPainterPath(), QRect(), }; } const QRect bbox( rawBbox.x_min, -rawBbox.y_max, rawBbox.x_max - rawBbox.x_min, rawBbox.y_max - rawBbox.y_min ); // 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, }; } QVector TtfParserFont::loadVariations() { if (!isOpen()) { throw tr("Font is not loaded."); } QVector variations; for (uint16_t i = 0; i < ttfp_get_variation_axes_count(m_face.get()); ++i) { ttfp_variation_axis axis; ttfp_get_variation_axis(m_face.get(), i, &axis); variations.append(VariationInfo { Tag(axis.tag).toString(), { static_cast(axis.tag) }, static_cast(axis.min_value), static_cast(axis.def_value), static_cast(axis.max_value), }); } return variations; } void TtfParserFont::setVariations(const QVector &variations) { if (!isOpen()) { throw tr("Font is not loaded."); } for (const auto &variation : variations) { ttfp_set_variation(m_face.get(), variation.tag.value, variation.value); } }