-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
/
Copy pathTextLayout.h
176 lines (144 loc) Β· 5.2 KB
/
TextLayout.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*
* Copyright (c) 2018-2020, Andreas Kling <[email protected]>
* Copyright (c) 2021, sin-ack <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteString.h>
#include <AK/CharacterTypes.h>
#include <AK/Forward.h>
#include <AK/Utf32View.h>
#include <AK/Utf8View.h>
#include <AK/Variant.h>
#include <AK/Vector.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/FontCascadeList.h>
#include <LibGfx/Forward.h>
#include <LibGfx/Rect.h>
#include <LibGfx/TextElision.h>
#include <LibGfx/TextWrapping.h>
namespace Gfx {
// FIXME: This currently isn't an ideal way of doing things; ideally, TextLayout
// would be doing the rendering by painting individual glyphs. However, this
// would regress our Unicode bidirectional text support. Therefore, fixing this
// requires:
// - Moving the bidirectional algorithm either here, or some place TextLayout
// can access;
// - Making TextLayout render the given text into something like a Vector<Line>
// where:
// using Line = Vector<DirectionalRun>;
// struct DirectionalRun {
// Utf32View glyphs;
// Vector<int> advance;
// TextDirection direction;
// };
// - Either;
// a) Making TextLayout output these Lines directly using a given Painter, or
// b) Taking the Lines from TextLayout and painting each glyph.
class TextLayout {
public:
TextLayout(Gfx::Font const& font, Utf8View const& text, FloatRect const& rect)
: m_font(font)
, m_font_metrics(font.pixel_metrics())
, m_text(text)
, m_rect(rect)
{
}
Vector<ByteString, 32> lines(TextElision elision, TextWrapping wrapping) const
{
return wrap_lines(elision, wrapping);
}
FloatRect bounding_rect(TextWrapping) const;
private:
Vector<ByteString, 32> wrap_lines(TextElision, TextWrapping) const;
ByteString elide_text_from_right(Utf8View) const;
Font const& m_font;
FontPixelMetrics m_font_metrics;
Utf8View m_text;
FloatRect m_rect;
};
inline bool should_paint_as_space(u32 code_point)
{
return is_ascii_space(code_point) || code_point == 0xa0;
}
enum class IncludeLeftBearing {
Yes,
No
};
struct DrawGlyph {
FloatPoint position;
u32 code_point;
void translate_by(FloatPoint const& delta)
{
position.translate_by(delta);
}
};
struct DrawEmoji {
FloatPoint position;
Gfx::Bitmap const* emoji;
void translate_by(FloatPoint const& delta)
{
position.translate_by(delta);
}
};
using DrawGlyphOrEmoji = Variant<DrawGlyph, DrawEmoji>;
class GlyphRun : public RefCounted<GlyphRun> {
public:
enum class TextType {
Common,
ContextDependent,
EndPadding,
Ltr,
Rtl,
};
GlyphRun(Vector<Gfx::DrawGlyphOrEmoji>&& glyphs, NonnullRefPtr<Font> font, TextType text_type)
: m_glyphs(move(glyphs))
, m_font(move(font))
, m_text_type(text_type)
{
}
[[nodiscard]] Font const& font() const { return m_font; }
[[nodiscard]] TextType text_type() const { return m_text_type; }
[[nodiscard]] Vector<Gfx::DrawGlyphOrEmoji> const& glyphs() const { return m_glyphs; }
[[nodiscard]] Vector<Gfx::DrawGlyphOrEmoji>& glyphs() { return m_glyphs; }
[[nodiscard]] bool is_empty() const { return m_glyphs.is_empty(); }
void append(Gfx::DrawGlyphOrEmoji glyph) { m_glyphs.append(glyph); }
private:
Vector<Gfx::DrawGlyphOrEmoji> m_glyphs;
NonnullRefPtr<Font> m_font;
TextType m_text_type;
};
Variant<DrawGlyph, DrawEmoji> prepare_draw_glyph_or_emoji(FloatPoint point, Utf8CodePointIterator& it, Font const& font);
template<typename Callback>
void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, Gfx::Font const& font, Callback callback, IncludeLeftBearing include_left_bearing = IncludeLeftBearing::No, Optional<float&> width = {})
{
float space_width = font.glyph_width(' ') + font.glyph_spacing();
u32 last_code_point = 0;
auto point = baseline_start;
for (auto code_point_iterator = string.begin(); code_point_iterator != string.end(); ++code_point_iterator) {
auto it = code_point_iterator; // The callback function will advance the iterator, so create a copy for this lookup.
auto code_point = *code_point_iterator;
point.set_y(baseline_start.y() - font.pixel_metrics().ascent);
if (should_paint_as_space(code_point)) {
point.translate_by(space_width, 0);
last_code_point = code_point;
continue;
}
auto kerning = font.glyphs_horizontal_kerning(last_code_point, code_point);
if (kerning != 0.0f)
point.translate_by(kerning, 0);
auto glyph_width = font.glyph_or_emoji_width(it) + font.glyph_spacing();
auto glyph_or_emoji = prepare_draw_glyph_or_emoji(point, code_point_iterator, font);
if (include_left_bearing == IncludeLeftBearing::Yes) {
if (glyph_or_emoji.has<DrawGlyph>())
glyph_or_emoji.get<DrawGlyph>().position += FloatPoint(font.glyph_left_bearing(code_point), 0);
}
callback(glyph_or_emoji);
point.translate_by(glyph_width, 0);
last_code_point = code_point;
}
if (width.has_value())
*width = point.x() - font.glyph_spacing();
}
}