Line data Source code
1 : /**
2 : Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "SPFontTextLayout.h"
24 :
25 : namespace STAPPLER_VERSIONIZED stappler::font {
26 :
27 8063 : inline static bool isSpaceOrLineBreak(char16_t c) {
28 8063 : return c == char16_t(0x0A) || chars::isspace(c);
29 : }
30 :
31 : template <typename Interface>
32 0 : static geom::Rect getLabelLineStartRect(const TextLayoutData<Interface> &f, uint16_t lineId, float density, uint32_t c) {
33 0 : geom::Rect rect;
34 0 : const LineLayoutData &line = f.lines.at(lineId);
35 0 : if (line.count > 0) {
36 0 : const CharLayoutData & firstChar = f.chars.at(std::max(line.start, c));
37 0 : const CharLayoutData & lastChar = f.chars.at(line.start + line.count - 1);
38 0 : rect.origin = geom::Vec2((firstChar.pos) / density, (line.pos) / density - line.height / density);
39 0 : rect.size = geom::Size2((lastChar.pos + lastChar.advance - firstChar.pos) / density, line.height / density);
40 : }
41 :
42 0 : return rect;
43 : }
44 :
45 : template <typename Interface>
46 0 : static geom::Rect getLabelLineEndRect(const TextLayoutData<Interface> &f, uint16_t lineId, float density, uint32_t c) {
47 0 : geom::Rect rect;
48 0 : const LineLayoutData &line = f.lines.at(lineId);
49 0 : if (line.count > 0) {
50 0 : const CharLayoutData & firstChar = f.chars.at(line.start);
51 0 : const CharLayoutData & lastChar = f.chars.at(std::min(line.start + line.count - 1, c));
52 0 : rect.origin = geom::Vec2((firstChar.pos) / density, (line.pos) / density - line.height / density);
53 0 : rect.size = geom::Size2((lastChar.pos + lastChar.advance - firstChar.pos) / density, line.height / density);
54 : }
55 0 : return rect;
56 : }
57 :
58 : template <typename Interface>
59 25 : static geom::Rect getCharsRect(const TextLayoutData<Interface> &f, uint32_t lineId, uint32_t firstCharId, uint32_t lastCharId, float density) {
60 25 : geom::Rect rect;
61 25 : const LineLayoutData & line = f.lines.at(lineId);
62 25 : const CharLayoutData & firstChar = f.chars.at(firstCharId);
63 25 : const CharLayoutData & lastChar = f.chars.at(lastCharId);
64 25 : rect.origin = geom::Vec2((firstChar.pos) / density, (line.pos) / density - line.height / density);
65 25 : rect.size = geom::Size2((lastChar.pos + lastChar.advance - firstChar.pos) / density, line.height / density);
66 25 : return rect;
67 : }
68 :
69 : template <typename Interface>
70 130 : static void TextLayoutData_str(const TextLayoutData<Interface> &f, const Callback<void(char16_t)> &cb, bool filter) {
71 1153 : for (auto it = f.begin(); it != f.end(); ++ it) {
72 1023 : const RangeLayoutData &range = *it.range;
73 1023 : if (!filter || range.align == VerticalAlign::Baseline) {
74 1023 : size_t end = it.start() + it.count() - 1;
75 12413 : for (size_t i = it.start(); i <= end; ++ i) {
76 11390 : const auto &spec = f.chars[i];
77 11390 : if (spec.charID != char16_t(0xAD) && spec.charID != char16_t(0xFFFF)) {
78 11390 : cb(spec.charID);
79 : }
80 : }
81 : }
82 : }
83 130 : }
84 :
85 : template <typename Interface>
86 130 : static void TextLayoutData_str(const TextLayoutData<Interface> &f, const Callback<void(char16_t)> &cb,
87 : uint32_t s_start, uint32_t s_end, size_t maxWords, bool ellipsis, bool filter) {
88 1153 : for (auto it = f.begin(); it != f.end(); ++ it) {
89 1023 : const RangeLayoutData &range = *it.range;
90 1023 : if (!filter || range.align == VerticalAlign::Baseline) {
91 5 : size_t end = it.start() + it.count() - 1;
92 60 : for (size_t i = it.start(); i <= end; ++ i) {
93 55 : const auto &spec = f.chars[i];
94 55 : if (spec.charID != char16_t(0xAD) && spec.charID != char16_t(0xFFFF)) {
95 55 : cb(spec.charID);
96 : }
97 : }
98 : }
99 : }
100 130 : }
101 :
102 : template <typename Interface>
103 532 : static Pair<uint32_t, CharSelectMode> TextLayoutData_getChar(const TextLayoutData<Interface> &f, int32_t x, int32_t y, CharSelectMode mode) {
104 532 : int32_t yDistance = maxOf<int32_t>();
105 532 : const LineLayoutData *pLine = nullptr;
106 532 : if (!f.lines.empty()) {
107 1088 : for (auto &l : f.lines) {
108 1088 : int32_t dst = maxOf<int32_t>();
109 1088 : switch (mode) {
110 260 : case CharSelectMode::Center:
111 260 : dst = abs(y - (l.pos - l.height / 2));
112 260 : break;
113 308 : case CharSelectMode::Best:
114 308 : dst = abs(y - (l.pos - l.height * 3 / 4));
115 308 : break;
116 520 : case CharSelectMode::Prefix:
117 : case CharSelectMode::Suffix:
118 520 : dst = abs(y - (l.pos - l.height));
119 520 : break;
120 : };
121 1088 : if (dst < yDistance) {
122 556 : pLine = &l;
123 556 : yDistance = dst;
124 : } else {
125 532 : break;
126 : }
127 : }
128 :
129 532 : if (f.chars.back().charID == char16_t(0x0A) && pLine == &f.lines.back() && (mode == CharSelectMode::Best || mode == CharSelectMode::Suffix)) {
130 0 : int32_t dst = maxOf<int32_t>();
131 0 : switch (mode) {
132 0 : case CharSelectMode::Center:
133 0 : dst = abs(y - (f.height - pLine->height / 2));
134 0 : break;
135 0 : case CharSelectMode::Best:
136 0 : dst = abs(y - (f.height - pLine->height * 3 / 4));
137 0 : break;
138 0 : case CharSelectMode::Prefix:
139 : case CharSelectMode::Suffix:
140 0 : dst = abs(y - (f.height - pLine->height));
141 0 : break;
142 : };
143 0 : if (dst < yDistance) {
144 0 : return pair(f.chars.size() - 1, CharSelectMode::Suffix);
145 : }
146 : }
147 : }
148 :
149 532 : if (!pLine) {
150 0 : return pair(maxOf<uint32_t>(), mode);
151 : }
152 :
153 532 : if (yDistance > pLine->height * 3 / 2 && mode != CharSelectMode::Best) {
154 0 : return pair(maxOf<uint32_t>(), mode);
155 : }
156 :
157 532 : CharSelectMode nextMode = mode;
158 532 : int32_t xDistance = maxOf<int32_t>();
159 532 : const CharLayoutData *pChar = nullptr;
160 532 : uint32_t charNumber = pLine->start;
161 5144 : for (uint32_t i = pLine->start; i < pLine->start + pLine->count; ++ i) {
162 5087 : auto &c = f.chars[i];
163 5087 : if (c.charID != char16_t(0xAD) && !isSpaceOrLineBreak(c.charID)) {
164 4486 : int32_t dst = maxOf<int32_t>();
165 4486 : CharSelectMode dstMode = mode;
166 4486 : switch (mode) {
167 1128 : case CharSelectMode::Center: dst = abs(x - (c.pos + c.advance / 2)); break;
168 1128 : case CharSelectMode::Prefix: dst = abs(x - c.pos); break;
169 1013 : case CharSelectMode::Suffix: dst = abs(x - (c.pos + c.advance)); break;
170 1217 : case CharSelectMode::Best: {
171 1217 : int32_t prefixDst = abs(x - c.pos);
172 1217 : int32_t suffixDst = abs(x - (c.pos + c.advance));
173 1217 : if (prefixDst <= suffixDst) {
174 128 : dst = prefixDst; dstMode = CharSelectMode::Prefix;
175 : } else {
176 1089 : dst = suffixDst; dstMode = CharSelectMode::Suffix;
177 : }
178 1217 : } break;
179 : };
180 4486 : if (dst < xDistance) {
181 4011 : pChar = &c;
182 4011 : xDistance = dst;
183 4011 : charNumber = i;
184 4011 : nextMode = dstMode;
185 : } else {
186 475 : break;
187 : }
188 : }
189 : }
190 532 : if (pLine->count && f.chars[pLine->start + pLine->count - 1].charID == char16_t(0x0A)) {
191 532 : auto &c = f.chars[pLine->start + pLine->count - 1];
192 532 : int32_t dst = maxOf<int32_t>();
193 532 : switch (mode) {
194 130 : case CharSelectMode::Prefix: dst = abs(x - c.pos); break;
195 142 : case CharSelectMode::Best: dst = abs(x - c.pos); break;
196 260 : default: break;
197 : };
198 532 : if (dst < xDistance) {
199 14 : pChar = &c;
200 14 : xDistance = dst;
201 14 : charNumber = pLine->start + pLine->count - 1;
202 14 : nextMode = CharSelectMode::Prefix;
203 : }
204 : }
205 :
206 532 : if ((mode == CharSelectMode::Best || mode == CharSelectMode::Suffix) && pLine == &(f.lines.back())) {
207 0 : auto c = f.chars.back();
208 0 : int32_t dst = abs(x - (c.pos + c.advance));
209 0 : if (dst < xDistance) {
210 0 : pChar = &c;
211 0 : xDistance = dst;
212 0 : charNumber = uint32_t(f.chars.size() - 1);
213 0 : nextMode = CharSelectMode::Suffix;
214 : }
215 : }
216 :
217 532 : if (!pChar) {
218 0 : return pair(maxOf<uint32_t>(), mode);
219 : }
220 :
221 532 : return pair(charNumber, nextMode);
222 : }
223 :
224 : template <typename Interface>
225 125 : const LineLayoutData *TextLayoutData_getLine(const TextLayoutData<Interface> &f, uint32_t idx) {
226 125 : const LineLayoutData *ret = nullptr;
227 125 : for (const LineLayoutData &it : f.lines) {
228 125 : if (it.start <= idx && it.start + it.count > idx) {
229 125 : ret = ⁢
230 125 : break;
231 : }
232 : }
233 125 : return ret;
234 : }
235 :
236 : template <typename Interface>
237 180 : uint32_t TextLayoutData_getLineNumber(const TextLayoutData<Interface> &f, uint32_t id) {
238 180 : uint16_t n = 0;
239 440 : for (auto &it : f.lines) {
240 440 : if (id >= it.start && id < it.start + it.count) {
241 180 : return n;
242 : }
243 260 : n++;
244 : }
245 0 : if (n >= f.lines.size()) {
246 0 : n = f.lines.size() - 1;
247 : }
248 0 : return n;
249 : }
250 :
251 : template <typename Interface>
252 0 : float TextLayoutData_getLinePosition(const TextLayoutData<Interface> &f, uint32_t firstCharId, uint32_t lastCharId, float density) {
253 0 : auto firstLine = TextLayoutData_getLine(f, firstCharId);
254 0 : auto lastLine = TextLayoutData_getLine(f, lastCharId);
255 :
256 0 : return ((firstLine->pos) / density + (lastLine->pos) / density) / 2.0f;
257 : }
258 :
259 : template <typename Interface>
260 142 : Pair<uint32_t, uint32_t> TextLayoutData_selectWord(const TextLayoutData<Interface> &f, uint32_t origin) {
261 142 : Pair<uint32_t, uint32_t> ret(origin, origin);
262 1863 : while (ret.second + 1 < f.chars.size() && !isSpaceOrLineBreak(f.chars[ret.second + 1].charID)) {
263 1721 : ++ ret.second;
264 : }
265 1113 : while (ret.first > 0 && !isSpaceOrLineBreak(f.chars[ret.first - 1].charID)) {
266 971 : -- ret.first;
267 : }
268 142 : return Pair<uint32_t, uint32_t>(ret.first, ret.second + 1 - ret.first);
269 : }
270 :
271 : template <typename Interface>
272 130 : geom::Rect TextLayoutData_getLineRect(const TextLayoutData<Interface> &f, uint32_t lineId, float density, const geom::Vec2 &origin) {
273 130 : if (lineId >= f.lines.size()) {
274 0 : return geom::Rect::ZERO;
275 : }
276 130 : return TextLayoutData_getLineRect(f, f.lines[lineId], density, origin);
277 : }
278 :
279 : template <typename Interface>
280 1153 : geom::Rect TextLayoutData_getLineRect(const TextLayoutData<Interface> &f, const LineLayoutData &line, float density, const geom::Vec2 &origin) {
281 1153 : geom::Rect rect;
282 1153 : if (line.count > 0) {
283 1153 : const CharLayoutData & firstChar = f.chars.at(line.start);
284 1153 : const CharLayoutData & lastChar = f.chars.at(line.start + line.count - 1);
285 1153 : rect.origin = geom::Vec2((firstChar.pos) / density + origin.x, (line.pos) / density - line.height / density + origin.y);
286 1153 : rect.size = geom::Size2((lastChar.pos + lastChar.advance - firstChar.pos) / density, line.height / density);
287 : }
288 1153 : return rect;
289 : }
290 :
291 : template <typename Interface>
292 25 : void TextLayoutData_getLabelRects(const TextLayoutData<Interface> &f, const Callback<void(geom::Rect)> &cb,
293 : uint32_t firstCharId, uint32_t lastCharId, float density, const geom::Vec2 &origin, const geom::Padding &p) {
294 25 : auto firstLine = TextLayoutData_getLineNumber(f, firstCharId);
295 25 : auto lastLine = TextLayoutData_getLineNumber(f, lastCharId);
296 :
297 25 : if (firstLine == lastLine) {
298 25 : auto rect = getCharsRect(f, firstLine, firstCharId, lastCharId, density);
299 25 : rect.origin.x += origin.x - p.left;
300 25 : rect.origin.y += origin.y - p.top;
301 25 : rect.size.width += p.left + p.right;
302 25 : rect.size.height += p.bottom + p.top;
303 25 : if (!rect.equals(geom::Rect::ZERO)) {
304 25 : cb(rect);
305 : }
306 : } else {
307 0 : auto first = getLabelLineStartRect(f, firstLine, density, firstCharId);
308 0 : if (!first.equals(geom::Rect::ZERO)) {
309 0 : first.origin.x += origin.x;
310 0 : first.origin.y += origin.y;
311 0 : if (first.origin.x - p.left < 0.0f) {
312 0 : first.size.width += (first.origin.x);
313 0 : first.origin.x = 0.0f;
314 : } else {
315 0 : first.origin.x -= p.left;
316 0 : first.size.width += p.left;
317 : }
318 0 : first.origin.y -= p.top;
319 0 : first.size.height += p.bottom + p.top;
320 0 : cb(first);
321 : }
322 :
323 0 : for (auto i = firstLine + 1; i < lastLine; i++) {
324 0 : auto rect = f.getLineRect(i, density);
325 0 : rect.origin.x += origin.x;
326 0 : rect.origin.y += origin.y - p.top;
327 0 : rect.size.height += p.bottom + p.top;
328 0 : if (!rect.equals(geom::Rect::ZERO)) {
329 0 : cb(rect);
330 : }
331 : }
332 :
333 0 : auto last = getLabelLineEndRect(f, lastLine, density, lastCharId);
334 0 : if (!last.equals(geom::Rect::ZERO)) {
335 0 : last.origin.x += origin.x;
336 0 : last.origin.y += origin.y - p.top;
337 0 : last.size.width += p.right;
338 0 : last.size.height += p.bottom + p.top;
339 0 : cb(last);
340 : }
341 : }
342 25 : }
343 :
344 : template <>
345 782 : void TextLayoutData<memory::StandartInterface>::reserve(size_t nchars, size_t nranges) {
346 782 : if (nchars) {
347 522 : chars.reserve(nchars);
348 522 : lines.reserve(nchars / 60);
349 : }
350 782 : if (nranges) {
351 0 : ranges.reserve(nranges);
352 : }
353 782 : }
354 :
355 : template <>
356 0 : void TextLayoutData<memory::PoolInterface>::reserve(size_t nchars, size_t nranges) {
357 0 : if (nchars) {
358 0 : chars.reserve(nchars);
359 0 : lines.reserve(nchars / 60);
360 : }
361 0 : if (nranges) {
362 0 : ranges.reserve(nranges);
363 : }
364 0 : }
365 :
366 : template <>
367 130 : void TextLayoutData<memory::StandartInterface>::str(const Callback<void(char16_t)> &cb, bool filter) const {
368 130 : TextLayoutData_str(*this, cb, filter);
369 130 : }
370 :
371 : template <>
372 0 : void TextLayoutData<memory::PoolInterface>::str(const Callback<void(char16_t)> &cb, bool filter) const {
373 0 : TextLayoutData_str(*this, cb, filter);
374 0 : }
375 :
376 : template <>
377 130 : void TextLayoutData<memory::StandartInterface>::str(const Callback<void(char16_t)> &cb,
378 : uint32_t s_start, uint32_t s_end, size_t maxWords, bool ellipsis, bool filter) const {
379 130 : TextLayoutData_str(*this, cb, s_start, s_end, maxWords, ellipsis, filter);
380 130 : }
381 :
382 : template <>
383 0 : void TextLayoutData<memory::PoolInterface>::str(const Callback<void(char16_t)> &cb,
384 : uint32_t s_start, uint32_t s_end, size_t maxWords, bool ellipsis, bool filter) const {
385 0 : TextLayoutData_str(*this, cb, s_start, s_end, maxWords, ellipsis, filter);
386 0 : }
387 :
388 : template <>
389 532 : Pair<uint32_t, CharSelectMode> TextLayoutData<memory::StandartInterface>::getChar(int32_t x, int32_t y, CharSelectMode mode) const {
390 532 : return TextLayoutData_getChar(*this, x, y, mode);
391 : }
392 :
393 : template <>
394 0 : Pair<uint32_t, CharSelectMode> TextLayoutData<memory::PoolInterface>::getChar(int32_t x, int32_t y, CharSelectMode mode) const {
395 0 : return TextLayoutData_getChar(*this, x, y, mode);
396 : }
397 :
398 : template <>
399 125 : const LineLayoutData *TextLayoutData<memory::StandartInterface>::getLine(uint32_t charIndex) const {
400 125 : return TextLayoutData_getLine(*this, charIndex);
401 : }
402 :
403 : template <>
404 0 : const LineLayoutData *TextLayoutData<memory::PoolInterface>::getLine(uint32_t charIndex) const {
405 0 : return TextLayoutData_getLine(*this, charIndex);
406 : }
407 :
408 : template <>
409 130 : uint32_t TextLayoutData<memory::StandartInterface>::getLineForChar(uint32_t charIndex) const {
410 130 : return TextLayoutData_getLineNumber(*this, charIndex);
411 : }
412 :
413 : template <>
414 0 : uint32_t TextLayoutData<memory::PoolInterface>::getLineForChar(uint32_t charIndex) const {
415 0 : return TextLayoutData_getLineNumber(*this, charIndex);
416 : }
417 :
418 : template <>
419 0 : float TextLayoutData<memory::StandartInterface>::getLinePosition(uint32_t firstCharId, uint32_t lastCharId, float density) const {
420 0 : return TextLayoutData_getLinePosition(*this, firstCharId, lastCharId, density);
421 : }
422 :
423 : template <>
424 0 : float TextLayoutData<memory::PoolInterface>::getLinePosition(uint32_t firstCharId, uint32_t lastCharId, float density) const {
425 0 : return TextLayoutData_getLinePosition(*this, firstCharId, lastCharId, density);
426 : }
427 :
428 : template <>
429 142 : Pair<uint32_t, uint32_t> TextLayoutData<memory::StandartInterface>::selectWord(uint32_t originChar) const {
430 142 : return TextLayoutData_selectWord(*this, originChar);
431 : }
432 :
433 : template <>
434 0 : Pair<uint32_t, uint32_t> TextLayoutData<memory::PoolInterface>::selectWord(uint32_t originChar) const {
435 0 : return TextLayoutData_selectWord(*this, originChar);
436 : }
437 :
438 : template <>
439 130 : geom::Rect TextLayoutData<memory::StandartInterface>::getLineRect(uint32_t lineId, float density, const geom::Vec2 &origin) const {
440 130 : return TextLayoutData_getLineRect(*this, lineId, density, origin);
441 : }
442 :
443 : template <>
444 0 : geom::Rect TextLayoutData<memory::PoolInterface>::getLineRect(uint32_t lineId, float density, const geom::Vec2 &origin) const {
445 0 : return TextLayoutData_getLineRect(*this, lineId, density, origin);
446 : }
447 :
448 : template <>
449 1023 : geom::Rect TextLayoutData<memory::StandartInterface>::getLineRect(const LineLayoutData &line, float density, const geom::Vec2 &origin) const {
450 1023 : return TextLayoutData_getLineRect(*this, line, density, origin);
451 : }
452 :
453 : template <>
454 0 : geom::Rect TextLayoutData<memory::PoolInterface>::getLineRect(const LineLayoutData &line, float density, const geom::Vec2 &origin) const {
455 0 : return TextLayoutData_getLineRect(*this, line, density, origin);
456 : }
457 :
458 : template <>
459 25 : void TextLayoutData<memory::StandartInterface>::getLabelRects(const Callback<void(geom::Rect)> &cb, uint32_t first, uint32_t last, float density,
460 : const geom::Vec2 &origin, const geom::Padding &p) const {
461 25 : return TextLayoutData_getLabelRects(*this, cb, first, last, density, origin, p);
462 : }
463 :
464 : template <>
465 0 : void TextLayoutData<memory::PoolInterface>::getLabelRects(const Callback<void(geom::Rect)> &cb, uint32_t first, uint32_t last, float density,
466 : const geom::Vec2 &origin, const geom::Padding &p) const {
467 0 : return TextLayoutData_getLabelRects(*this, cb, first, last, density, origin, p);
468 : }
469 :
470 : }
|