File indexing completed on 2025-02-02 04:11:08

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #pragma once
0008 
0009 #include <QRawFont>
0010 #include <QFontMetricsF>
0011 
0012 #include "model/property/sub_object_property.hpp"
0013 #include "model/property/option_list_property.hpp"
0014 #include "model/property/reference_property.hpp"
0015 #include "shape.hpp"
0016 
0017 namespace glaxnimate::model {
0018 
0019 class Font : public Object
0020 {
0021     GLAXNIMATE_OBJECT(Font)
0022     GLAXNIMATE_PROPERTY_OPTIONS(QString, family, "", QStringList,
0023         &Font::families, &Font::on_family_changed, {}, PropertyTraits::Visual, OptionListPropertyBase::FontCombo)
0024     GLAXNIMATE_PROPERTY_OPTIONS(float, size, 32, QList<int>,
0025         &Font::standard_sizes, &Font::on_font_changed, {}, PropertyTraits::Visual, OptionListPropertyBase::LaxValues)
0026     GLAXNIMATE_PROPERTY_OPTIONS(QString, style, "", QStringList,
0027         &Font::styles, &Font::on_font_changed, &Font::valid_style, PropertyTraits::Visual)
0028     GLAXNIMATE_PROPERTY(float, line_height, 1, &Font::on_font_changed, {}, PropertyTraits::Visual|PropertyTraits::Percent)
0029 
0030 public:
0031     struct CharData
0032     {
0033         quint32 glyph;
0034         QPointF position;
0035     };
0036 
0037     struct LineData
0038     {
0039         std::vector<CharData> glyphs;
0040         QRectF bounds;
0041         QPointF baseline;
0042         QPointF advance;
0043         QString text;
0044     };
0045 
0046     using ParagraphData = std::vector<LineData>;
0047 
0048     using CharDataCache = std::unordered_map<quint32, QPainterPath>;
0049 
0050     explicit Font(Document* doc);
0051     ~Font();
0052 
0053     const QRawFont& raw_font() const;
0054     const QFont& query() const;
0055     const QFontMetricsF& metrics() const;
0056 
0057     void from_qfont(const QFont& f);
0058 
0059     QStringList styles() const;
0060     QStringList families() const;
0061     QList<int> standard_sizes() const;
0062 
0063     QString type_name_human() const override;
0064 
0065     ParagraphData layout(const QString& string) const;
0066 
0067     /**
0068      * \brief Distance between two baselines
0069      */
0070     qreal line_spacing() const;
0071 
0072     /**
0073      * \brief Distance between two baselines for a line_height of 1
0074      */
0075     qreal line_spacing_unscaled() const;
0076 
0077     QPainterPath path_for_glyph(quint32 glyph, CharDataCache& cache, bool fix_paint) const;
0078 
0079 Q_SIGNALS:
0080     void font_changed();
0081 
0082 protected:
0083     void on_transfer(model::Document* doc) override;
0084 
0085 private:
0086     void on_family_changed();
0087     void on_font_changed();
0088     void refresh_data(bool update_styles);
0089     bool valid_style(const QString& style);
0090 
0091     class Private;
0092     std::unique_ptr<Private> d;
0093 };
0094 
0095 class TextShape : public ShapeElement
0096 {
0097     GLAXNIMATE_OBJECT(TextShape)
0098     GLAXNIMATE_PROPERTY(QString, text, {}, &TextShape::on_text_changed, {}, PropertyTraits::Visual)
0099     GLAXNIMATE_ANIMATABLE(QPointF, position, QPointF())
0100     GLAXNIMATE_SUBOBJECT(Font, font)
0101     GLAXNIMATE_PROPERTY_REFERENCE(model::ShapeElement, path, &TextShape::valid_paths, &TextShape::is_valid_path, &TextShape::path_changed)
0102     GLAXNIMATE_ANIMATABLE(float, path_offset, 0, &TextShape::on_text_changed)
0103 
0104 public:
0105     explicit TextShape(model::Document* document);
0106 
0107     void add_shapes(FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const override;
0108 
0109     QPainterPath shape_data(FrameTime t) const;
0110 
0111     QIcon tree_icon() const override;
0112     QRectF local_bounding_rect(FrameTime t) const override;
0113     QString type_name_human() const override;
0114     std::unique_ptr<ShapeElement> to_path() const override;
0115 
0116     /**
0117      * \brief Position where the next character would be
0118      *
0119      * ie: where to add another TextShape to make them flow together
0120      */
0121     QPointF offset_to_next_character() const;
0122 
0123 protected:
0124     QPainterPath to_painter_path_impl(FrameTime t) const override;
0125 
0126 private:
0127     void on_font_changed();
0128     void on_text_changed();
0129     const QPainterPath& untranslated_path(FrameTime t) const;
0130 
0131     std::vector<DocumentNode*> valid_paths() const;
0132     bool is_valid_path(DocumentNode* node) const;
0133     void path_changed(model::ShapeElement* new_path, model::ShapeElement* old_path);
0134 
0135     mutable Font::CharDataCache cache;
0136     mutable QPainterPath shape_cache;
0137 };
0138 
0139 } // namespace glaxnimate::model