1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "typography_impl.h"
17
18 #include <cassert>
19 #include <functional>
20 #include <variant>
21
22 #include <unicode/ubidi.h>
23
24 #include "font_collection.h"
25 #include "shaper.h"
26 #include "texgine/any_span.h"
27 #include "texgine_exception.h"
28 #include "text_span.h"
29 #include "texgine/typography_types.h"
30 #include "texgine/utils/exlog.h"
31 #include "texgine/utils/trace.h"
32 #include "word_breaker.h"
33
34 namespace OHOS {
35 namespace Rosen {
36 namespace TextEngine {
37 #define MAXWIDTH 1e9
38 #define HALF 0.5f
39 #define MINDEV 1e-3
40 #define SUCCESSED 0
41 #define FAILED 1
42
43 namespace {
CreateEllipsisSpan(const TypographyStyle & ys,const std::shared_ptr<FontProviders> & fontProviders)44 std::vector<LineMetrics> CreateEllipsisSpan(const TypographyStyle &ys,
45 const std::shared_ptr<FontProviders> &fontProviders)
46 {
47 if (ys.ellipsis.empty()) {
48 return {};
49 }
50
51 TextStyle xs;
52 xs.fontSize = ys.lineStyle.fontSize;
53 xs.fontFamilies = ys.lineStyle.fontFamilies;
54
55 std::vector<VariantSpan> spans = {TextSpan::MakeFromText(ys.ellipsis)};
56 spans[0].SetTextStyle(xs);
57 auto ys2 = ys;
58 ys2.wordBreakType = WordBreakType::BREAK_ALL;
59 ys2.breakStrategy = BreakStrategy::GREEDY;
60 return Shaper::DoShape(spans, ys2, fontProviders, MAXWIDTH);
61 }
62 } // namespace
63
AddSpanAndUpdateMetrics(const VariantSpan & span)64 void LineMetrics::AddSpanAndUpdateMetrics(const VariantSpan &span)
65 {
66 lineSpans.push_back(span);
67 width += span.GetWidth();
68 }
69
Boundary(size_t left,size_t right)70 Boundary::Boundary(size_t left, size_t right)
71 {
72 leftIndex = left;
73 rightIndex = right;
74 }
75
TypographyImpl(TypographyStyle & ys,std::vector<VariantSpan> & spans,std::shared_ptr<FontProviders> providers)76 TypographyImpl::TypographyImpl(TypographyStyle &ys, std::vector<VariantSpan> &spans,
77 std::shared_ptr<FontProviders> providers
78 ): typographyStyle_(std::move(ys)), spans_(std::move(spans)), fontProviders_(providers)
79 {
80 }
81
GetAlphabeticBaseline() const82 double TypographyImpl::GetAlphabeticBaseline() const
83 {
84 if (lineMaxCoveredAscent_.empty()) {
85 return 0.0;
86 }
87
88 return lineMaxCoveredAscent_[0];
89 }
90
GetIdeographicBaseline() const91 double TypographyImpl::GetIdeographicBaseline() const
92 {
93 if (lineMaxCoveredAscent_.empty() || lineMaxCoveredDescent_.empty()) {
94 return 0.0;
95 }
96
97 return lineMaxCoveredAscent_[0] + lineMaxCoveredDescent_[0];
98 }
99
DidExceedMaxLines() const100 bool TypographyImpl::DidExceedMaxLines() const
101 {
102 return didExceedMaxLines_;
103 }
104
GetHeight() const105 double TypographyImpl::GetHeight() const
106 {
107 return height_;
108 }
109
GetMaxWidth() const110 double TypographyImpl::GetMaxWidth() const
111 {
112 return maxWidth_;
113 }
114
GetActualWidth() const115 double TypographyImpl::GetActualWidth() const
116 {
117 return maxLineWidth_;
118 }
119
GetMaxIntrinsicWidth() const120 double TypographyImpl::GetMaxIntrinsicWidth() const
121 {
122 return maxIntrinsicWidth_;
123 }
124
GetMinIntrinsicWidth() const125 double TypographyImpl::GetMinIntrinsicWidth() const
126 {
127 return minIntrinsicWidth_;
128 }
129
GetLineCount() const130 int TypographyImpl::GetLineCount() const
131 {
132 return lineMetrics_.size();
133 }
134
SetIndents(const std::vector<float> & indents)135 void TypographyImpl::SetIndents(const std::vector<float> &indents)
136 {
137 // to be done: set indents
138 }
139
FindGlyphTargetLine(double y) const140 size_t TypographyImpl::FindGlyphTargetLine(double y) const
141 {
142 int targetLine = 0;
143 for (auto i = 0; i < static_cast<int>(lineMetrics_.size()); i++) {
144 if (y < baselines_[i] + lineMaxCoveredDescent_[i]) {
145 break;
146 }
147 targetLine = i + 1;
148 }
149 return targetLine;
150 }
151
FindGlyphTargetIndex(size_t line,double x,double & offsetX,std::vector<double> & widths) const152 size_t TypographyImpl::FindGlyphTargetIndex(size_t line,
153 double x, double &offsetX, std::vector<double> &widths) const
154 {
155 // gather widths
156 widths = { lineMetrics_[line].indent };
157 for (const auto &vs : lineMetrics_[line].lineSpans) {
158 if (vs == nullptr) {
159 continue;
160 }
161
162 auto ws = vs.GetGlyphWidths();
163 widths.insert(widths.end(), ws.begin(), ws.end());
164 }
165
166 offsetX = 0;
167 size_t targetIndex = 0;
168 for (const auto &width : widths) {
169 if (x < offsetX + width) {
170 break;
171 }
172
173 targetIndex++;
174 offsetX += width;
175 }
176 return targetIndex;
177 }
178
GetGlyphIndexByCoordinate(double x,double y) const179 IndexAndAffinity TypographyImpl::GetGlyphIndexByCoordinate(double x, double y) const
180 {
181 std::stringstream ss;
182 ss << "GetGlyphPositionAtCoordinate(" << x << "," << y << ")";
183 LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), ss.str());
184
185 // process y < 0
186 if (fabs(height_) < DBL_EPSILON || y < 0) {
187 LOGEX_FUNC_LINE_DEBUG() << "special: y < 0";
188 return {0, Affinity::NEXT};
189 }
190
191 // find targetLine
192 int targetLine = static_cast<int>(FindGlyphTargetLine(y));
193 LOGEX_FUNC_LINE_DEBUG() << "targetLine: " << targetLine;
194
195 // count glyph before targetLine
196 size_t count = 0;
197 for (auto i = 0; i < targetLine; i++) {
198 for (const auto &span : lineMetrics_[i].lineSpans) {
199 count += span.GetNumberOfCharGroup();
200 }
201 }
202 LOGEX_FUNC_LINE_DEBUG() << "count: " << count;
203
204 // process y more than typography
205 if (targetLine == static_cast<int>(lineMetrics_.size())) {
206 LOGEX_FUNC_LINE_DEBUG() << "special: y >= max";
207 return {count - 1, Affinity::PREV};
208 }
209
210 // find targetIndex
211 double offsetX = 0;
212 std::vector<double> widths;
213 auto targetIndex = FindGlyphTargetIndex(targetLine, x, offsetX, widths);
214 count += targetIndex;
215 LOGEX_FUNC_LINE_DEBUG() << "targetIndex: " << targetIndex;
216
217 // process first line left part
218 if (targetIndex == 0 && targetLine == 0) {
219 LOGEX_FUNC_LINE_DEBUG() << "special: first line left part";
220 return {0, Affinity::NEXT};
221 }
222
223 // process right part
224 if (targetIndex == widths.size()) {
225 count--;
226 }
227
228 // calc affinity
229 auto affinity = Affinity::PREV;
230 if (targetIndex > 0 && targetIndex < widths.size()) {
231 auto mid = offsetX + widths[targetIndex] * HALF;
232 affinity = x < mid ? Affinity::NEXT : Affinity::PREV;
233 }
234 LOGEX_FUNC_LINE_DEBUG() << "affinity: " << (affinity == Affinity::PREV ? "upstream" : "downstream");
235
236 return {count - 1, affinity};
237 }
238
ComputeWordBoundary() const239 void TypographyImpl::ComputeWordBoundary() const
240 {
241 if (!boundariesCache_.empty()) {
242 return;
243 }
244
245 for (const auto &span : spans_) {
246 auto offset = boundariesCache_.empty() ? 0 : boundariesCache_.back().rightIndex;
247 if (span.TryToAnySpan()) {
248 boundariesCache_.emplace_back(offset, offset + 1);
249 continue;
250 }
251
252 if (const auto &ts = span.TryToTextSpan(); ts != nullptr) {
253 WordBreaker wb;
254 wb.SetLocale(icu::Locale::createFromName(span.GetTextStyle().locale.c_str()));
255 wb.SetRange(0, ts->u16vect_.size());
256 auto boundaries = wb.GetBoundary(ts->u16vect_, true);
257 for (const auto &[left, right] : boundaries) {
258 boundariesCache_.emplace_back(left + offset, right + offset);
259 }
260 }
261 }
262 }
263
GetWordBoundaryByIndex(size_t index) const264 Boundary TypographyImpl::GetWordBoundaryByIndex(size_t index) const
265 {
266 ComputeWordBoundary();
267 if (boundariesCache_.empty()) {
268 return {0, 0};
269 }
270
271 for (const auto &boundary : boundariesCache_) {
272 if (boundary.leftIndex <= index && index < boundary.rightIndex) {
273 return boundary;
274 }
275 }
276
277 auto right = boundariesCache_.back().rightIndex;
278 return {right, right};
279 }
280
Layout(double maxWidth)281 void TypographyImpl::Layout(double maxWidth)
282 {
283 boundariesCache_ = {};
284 try {
285 ScopedTrace scope("TypographyImpl::Layout");
286 LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "TypographyImpl::Layout");
287 LOGEX_FUNC_LINE(INFO) << "Layout maxWidth: " << maxWidth << ", spans.size(): " << spans_.size();
288 maxWidth_ = maxWidth;
289
290 lineMetrics_ = Shaper::DoShape(spans_, typographyStyle_, fontProviders_, maxWidth);
291 if (lineMetrics_.size() == 0) {
292 LOGEX_FUNC_LINE(ERROR) << "Shape failed";
293 return;
294 }
295
296 ComputeIntrinsicWidth();
297
298 ConsiderEllipsis();
299 auto ret = ComputeStrut();
300 if (ret) {
301 LOGEX_FUNC_LINE(ERROR) << "ComputeStrut failed";
302 return;
303 }
304
305 ret = UpdateMetrics();
306 if (ret) {
307 LOGEX_FUNC_LINE(ERROR) << "UpdateMetrics failed";
308 return;
309 }
310
311 DoLayout();
312 ApplyAlignment();
313 } catch (struct TexgineException &e) {
314 LOGEX_FUNC_LINE(ERROR) << "catch exception: " << e.message;
315 }
316 }
317
ComputeIntrinsicWidth()318 void TypographyImpl::ComputeIntrinsicWidth()
319 {
320 maxIntrinsicWidth_ = 0.0;
321 minIntrinsicWidth_ = 0.0;
322 double lastInvisibleWidth = 0;
323 for (const auto &line : lineMetrics_) {
324 for (const auto &span : line.lineSpans) {
325 if (span == nullptr) {
326 continue;
327 }
328
329 auto width = span.GetWidth();
330 auto visibleWidth = span.GetVisibleWidth();
331 maxIntrinsicWidth_ += width;
332 minIntrinsicWidth_ = std::max(visibleWidth, minIntrinsicWidth_);
333 lastInvisibleWidth = width - visibleWidth;
334 }
335 }
336
337 maxIntrinsicWidth_ -= lastInvisibleWidth;
338 if (typographyStyle_.maxLines > 1) {
339 minIntrinsicWidth_ = std::min(maxIntrinsicWidth_, minIntrinsicWidth_);
340 } else {
341 minIntrinsicWidth_ = maxIntrinsicWidth_;
342 }
343 }
344
ConsiderEllipsis()345 void TypographyImpl::ConsiderEllipsis()
346 {
347 didExceedMaxLines_ = false;
348 auto maxLines = typographyStyle_.maxLines;
349 if (lineMetrics_.size() <= maxLines) {
350 return;
351 }
352 lineMetrics_.erase(lineMetrics_.begin() + maxLines, lineMetrics_.end());
353
354 std::vector<LineMetrics> ellipsisMertics = CreateEllipsisSpan(typographyStyle_, fontProviders_);
355 double ellipsisWidth = 0.0;
356 std::vector<VariantSpan> ellipsisSpans;
357 for (auto &metric : ellipsisMertics) {
358 for (auto &es : metric.lineSpans) {
359 ellipsisWidth += es.GetWidth();
360 ellipsisSpans.push_back(es);
361 }
362 }
363
364 double width = 0;
365 auto &lastline = lineMetrics_[maxLines - 1];
366 for (const auto &span : lastline.lineSpans) {
367 width += span.GetWidth();
368 }
369
370 // protected the first span and ellipsis
371 while (static_cast<int>(width) > static_cast<int>(maxWidth_ - ellipsisWidth) &&
372 static_cast<int>(lastline.lineSpans.size()) > 1) {
373 width -= lastline.lineSpans.back().GetWidth();
374 lastline.lineSpans.pop_back();
375 }
376
377 // Add ellipsisSpans
378 lastline.lineSpans.insert(lastline.lineSpans.end(), ellipsisSpans.begin(), ellipsisSpans.end());
379 didExceedMaxLines_ = true;
380 }
381
UpdateMetrics()382 int TypographyImpl::UpdateMetrics()
383 {
384 LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "UpdateMetrics");
385 baselines_ = {};
386 lineMaxAscent_ = {};
387 lineMaxCoveredAscent_ = {};
388 lineMaxCoveredDescent_ = {};
389 height_ = 0.0;
390
391 for (auto i = 0; i < static_cast<int>(lineMetrics_.size()); i++) {
392 lineMaxAscent_.push_back(strut_.ascent);
393 lineMaxCoveredAscent_.push_back(strut_.ascent + strut_.halfLeading);
394 lineMaxCoveredDescent_.push_back(strut_.descent + strut_.halfLeading);
395
396 for (auto &span : lineMetrics_[i].lineSpans) {
397 if (span == nullptr) {
398 LOGEX_FUNC_LINE(ERROR) << "span is nullptr";
399 return FAILED;
400 }
401
402 double coveredAscent = 0;
403 auto ret = UpdateSpanMetrics(span, coveredAscent);
404 if (ret) {
405 LOGEX_FUNC_LINE(ERROR) << "UpdateMerics is failed";
406 return FAILED;
407 }
408
409 if (auto as = span.TryToAnySpan(); as != nullptr) {
410 span.AdjustOffsetY(-coveredAscent);
411 }
412 }
413
414 height_ += lineMaxCoveredAscent_.back() + lineMaxCoveredDescent_.back();
415 baselines_.push_back(height_ - lineMaxCoveredDescent_.back());
416 LOGEX_FUNC_LINE_DEBUG() << "[" << i << "] ascent: " << lineMaxAscent_.back() <<
417 ", coveredAscent: " << lineMaxCoveredAscent_.back() <<
418 ", coveredDescent: " << lineMaxCoveredDescent_.back();
419 }
420
421 return SUCCESSED;
422 }
423
DoLayout()424 void TypographyImpl::DoLayout()
425 {
426 maxLineWidth_ = 0.0;
427 for (auto i = 0; i < static_cast<int>(lineMetrics_.size()); i++) {
428 double offsetX = 0;
429 for (auto &vs : lineMetrics_[i].lineSpans) {
430 vs.AdjustOffsetY(baselines_[i]);
431 vs.AdjustOffsetX(offsetX);
432 offsetX += vs.GetWidth();
433
434 lineMetrics_[i].width = offsetX;
435 }
436 maxLineWidth_ = std::max(maxLineWidth_, lineMetrics_[i].width);
437 }
438 }
439
ComputeStrut()440 int TypographyImpl::ComputeStrut()
441 {
442 strut_ = {};
443
444 bool strutValid = typographyStyle_.useLineStyle && typographyStyle_.lineStyle.fontSize >= 0;
445 if (!strutValid) {
446 return SUCCESSED;
447 }
448
449 auto fontCollection = fontProviders_->GenerateFontCollection(
450 typographyStyle_.lineStyle.fontFamilies);
451 if (fontCollection == nullptr) {
452 LOGEX_FUNC_LINE(ERROR) << "fontCollection is null";
453 return FAILED;
454 }
455
456 FontStyles style(typographyStyle_.lineStyle.fontWeight, typographyStyle_.lineStyle.fontStyle);
457 auto typeface = fontCollection->GetTypefaceForFontStyles(style, {}, {});
458 if (typeface == nullptr) {
459 LOGEX_FUNC_LINE_DEBUG() << "seek typeface failed";
460 return FAILED;
461 }
462
463 TexgineFontMetrics strutMetrics;
464 TexgineFont font;
465 font.SetTypeface(typeface->Get());
466 font.SetSize(typographyStyle_.lineStyle.fontSize);
467 font.GetMetrics(&strutMetrics);
468
469 double strutLeading = typographyStyle_.lineStyle.spacingScale.value_or(0) * typographyStyle_.lineStyle.fontSize;
470 auto leading = strutLeading;
471 if (typographyStyle_.lineStyle.heightOnly) {
472 double metricsHeight = -*strutMetrics.fAscent_ + *strutMetrics.fDescent_;
473 if (fabs(metricsHeight) < DBL_EPSILON) {
474 LOGEX_FUNC_LINE(ERROR) << "strutMetrics is error";
475 return FAILED;
476 }
477
478 double scale = typographyStyle_.lineStyle.heightScale * typographyStyle_.lineStyle.fontSize;
479 strut_.ascent = (-(*strutMetrics.fAscent_) / metricsHeight) * scale;
480 strut_.descent = (*strutMetrics.fDescent_ / metricsHeight) * scale;
481 } else {
482 strut_.ascent = -(*strutMetrics.fAscent_);
483 strut_.descent = *strutMetrics.fDescent_;
484 leading = fabs(leading) < DBL_EPSILON ? *strutMetrics.fLeading_ : strutLeading;
485 }
486 strut_.halfLeading = leading * HALF;
487 return SUCCESSED;
488 }
489
UpdateSpanMetrics(VariantSpan & span,double & coveredAscent)490 int TypographyImpl::UpdateSpanMetrics(VariantSpan &span, double &coveredAscent)
491 {
492 auto style = span.GetTextStyle();
493 TexgineFontMetrics metrics;
494 if (auto ts = span.TryToTextSpan(); ts != nullptr) {
495 metrics = ts->tmetrics_;
496 } else {
497 auto as = span.TryToAnySpan();
498 auto families = style.fontFamilies;
499 if (families.empty()) {
500 families = typographyStyle_.fontFamilies;
501 }
502 auto fontCollection = fontProviders_->GenerateFontCollection(families);
503 if (fontCollection == nullptr) {
504 LOGEX_FUNC_LINE(ERROR) << "fontCollection is nullptr";
505 return FAILED;
506 }
507
508 FontStyles fs(style.fontWeight, style.fontStyle);
509 auto typeface = fontCollection->GetTypefaceForChar(0xFFFC, fs, "Latn", style.locale);
510 if (typeface == nullptr) {
511 typeface = fontCollection->GetTypefaceForFontStyles(fs, "Latn", style.locale);
512 }
513 if (typeface == nullptr) {
514 LOGEX_FUNC_LINE(ERROR) << "typeface is nullptr";
515 return FAILED;
516 }
517
518 TexgineFont font;
519 font.SetTypeface(typeface->Get());
520 font.SetSize(style.fontSize);
521 font.GetMetrics(&metrics);
522 }
523
524 if (DoUpdateSpanMetrics(span, metrics, style, coveredAscent)) {
525 LOGEX_FUNC_LINE(ERROR) << "DoUpdateSpanMetrics is error";
526 return FAILED;
527 }
528
529 return SUCCESSED;
530 }
531
DoUpdateSpanMetrics(const VariantSpan & span,const TexgineFontMetrics & metrics,const TextStyle & style,double & coveredAscent)532 int TypographyImpl::DoUpdateSpanMetrics(const VariantSpan &span, const TexgineFontMetrics &metrics,
533 const TextStyle &style, double &coveredAscent)
534 {
535 bool onlyUseStrut = typographyStyle_.useLineStyle;
536 onlyUseStrut = onlyUseStrut && (typographyStyle_.lineStyle.fontSize >= 0);
537 onlyUseStrut = onlyUseStrut && typographyStyle_.lineStyle.only;
538 double ascent = -*metrics.fAscent_;
539 if (!onlyUseStrut) {
540 double coveredDescent = 0;
541 if (style.heightOnly) {
542 double metricsHeight = -*metrics.fAscent_ + *metrics.fDescent_;
543 if (fabs(metricsHeight) < DBL_EPSILON) {
544 LOGEX_FUNC_LINE(ERROR) << "metrics is error";
545 return FAILED;
546 }
547
548 coveredAscent = (-*metrics.fAscent_ / metricsHeight) * style.heightScale * style.fontSize;
549 coveredDescent = (*metrics.fDescent_ / metricsHeight) * style.heightScale * style.fontSize;
550 } else {
551 coveredAscent = (-*metrics.fAscent_ + *metrics.fLeading_ * HALF);
552 coveredDescent = (*metrics.fDescent_ + *metrics.fLeading_ * HALF);
553 }
554 if (auto as = span.TryToAnySpan(); as != nullptr) {
555 UpadateAnySpanMetrics(as, coveredAscent, coveredDescent);
556 ascent = coveredAscent;
557 }
558 lineMaxCoveredAscent_.back() = std::max(coveredAscent, lineMaxCoveredAscent_.back());
559 lineMaxCoveredDescent_.back() = std::max(coveredDescent, lineMaxCoveredDescent_.back());
560 }
561 lineMaxAscent_.back() = std::max(lineMaxAscent_.back(), ascent);
562 return SUCCESSED;
563 }
564
UpadateAnySpanMetrics(std::shared_ptr<AnySpan> & span,double & coveredAscent,double & coveredDescent)565 void TypographyImpl::UpadateAnySpanMetrics(std::shared_ptr<AnySpan> &span, double &coveredAscent,
566 double &coveredDescent)
567 {
568 if (span == nullptr) {
569 throw TEXGINE_EXCEPTION(INVALID_ARGUMENT);
570 }
571
572 double as = coveredAscent;
573 double de = coveredDescent;
574 double aj = span->GetBaseline() == TextBaseline::IDEOGRAPHIC ? -de * HALF : 0;
575 double lo = span->GetLineOffset();
576 double he = span->GetHeight();
577
578 using CalcAscentFunc = std::function<double()>;
579 std::map<AnySpanAlignment, CalcAscentFunc> calcMap = {
580 {AnySpanAlignment::OFFSET_AT_BASELINE, [&] { return aj + lo; }},
581 {AnySpanAlignment::ABOVE_BASELINE, [&] { return aj + he; }},
582 {AnySpanAlignment::BELOW_BASELINE, [&] { return -aj; }},
583 {AnySpanAlignment::TOP_OF_ROW_BOX, [&] { return as; }},
584 {AnySpanAlignment::BOTTOM_OF_ROW_BOX, [&] { return he - de; }},
585 {AnySpanAlignment::CENTER_OF_ROW_BOX, [&] { return (as - de + he) * HALF; }},
586 };
587
588 coveredAscent = calcMap[span->GetAlignment()]();
589 coveredDescent = he - coveredAscent;
590 }
591
Paint(TexgineCanvas & canvas,double offsetX,double offsetY)592 void TypographyImpl::Paint(TexgineCanvas &canvas, double offsetX, double offsetY)
593 {
594 for (auto &metric : lineMetrics_) {
595 for (auto &span : metric.lineSpans) {
596 span.PaintShadow(canvas, offsetX + span.GetOffsetX(), offsetY + span.GetOffsetY());
597 }
598
599 for (auto &span : metric.lineSpans) {
600 span.Paint(canvas, offsetX + span.GetOffsetX(), offsetY + span.GetOffsetY());
601 }
602 }
603 }
604
GetTextRectsByBoundary(Boundary boundary,TextRectHeightStyle heightStyle,TextRectWidthStyle widthStyle) const605 std::vector<TextRect> TypographyImpl::GetTextRectsByBoundary(Boundary boundary, TextRectHeightStyle heightStyle,
606 TextRectWidthStyle widthStyle) const
607 {
608 if (boundary.leftIndex > boundary.rightIndex || boundary.leftIndex < 0 || boundary.rightIndex < 0) {
609 LOGEX_FUNC_LINE(ERROR) << "the box range is error";
610 return {};
611 }
612 std::vector<TextRect> totalBoxes;
613 for (auto i = 0; i < static_cast<int>(lineMetrics_.size()); i++) {
614 std::vector<TextRect> lineBoxes;
615 auto baseline = baselines_[i];
616 auto as = lineMaxAscent_[i];
617 auto ca = lineMaxCoveredAscent_[i];
618 auto cd = lineMaxCoveredDescent_[i];
619 auto hl = ca - as;
620 auto fl = i == 0 ? 1 : 0;
621 auto ll = i == static_cast<int>(lineMetrics_.size()) - 1 ? 1 : 0;
622 constexpr auto tgh = TextRectHeightStyle::TIGHT;
623 constexpr auto ctb = TextRectHeightStyle::COVER_TOP_AND_BOTTOM;
624 constexpr auto chf = TextRectHeightStyle::COVER_HALF_TOP_AND_BOTTOM;
625 constexpr auto ctp = TextRectHeightStyle::COVER_TOP;
626 constexpr auto cbm = TextRectHeightStyle::COVER_BOTTOM;
627
628 std::map<TextRectHeightStyle, std::function<struct CalcResult()>> calcMap = {
629 {tgh, [&] { return CalcResult{false}; }},
630 {ctb, [&] { return CalcResult{true, as, cd}; }},
631 {chf, [&] { return CalcResult{true, as + fl * hl * HALF, cd + ll * hl * HALF}; }},
632 {ctp, [&] { return CalcResult{true, as + fl * hl, cd}; }},
633 {cbm, [&] { return CalcResult{true, as, cd + ll * hl}; }},
634 {TextRectHeightStyle::FOLLOW_BY_LINE_STYLE, [&] {
635 if (typographyStyle_.useLineStyle && typographyStyle_.lineStyle.fontSize >= 0) {
636 return CalcResult{true, strut_.ascent, strut_.descent};
637 }
638 return CalcResult{false};
639 }},
640 };
641
642 const auto &result = calcMap[heightStyle]();
643 ComputeSpans(i, baseline, result, lineBoxes);
644 if (widthStyle == TextRectWidthStyle::MAX_WIDTH) {
645 if (lineBoxes.empty()) {
646 LOGEX_FUNC_LINE(ERROR) << "rects is null";
647 } else {
648 *lineBoxes.back().rect.fRight_ += maxLineWidth_ - lineMetrics_[i].width;
649 }
650 }
651 totalBoxes.insert(totalBoxes.end(), lineBoxes.begin(), lineBoxes.end());
652 }
653 return MergeRects(totalBoxes, boundary);
654 }
655
ComputeSpans(int lineIndex,double baseline,const CalcResult & calcResult,std::vector<TextRect> & lineBoxes) const656 void TypographyImpl::ComputeSpans(int lineIndex, double baseline, const CalcResult &calcResult,
657 std::vector<TextRect> &lineBoxes) const
658 {
659 for (const auto &span : lineMetrics_[lineIndex].lineSpans) {
660 if (span == nullptr) {
661 continue;
662 }
663
664 std::vector<TextRect> spanBoxes;
665 auto offsetX = span.GetOffsetX();
666 auto offsetY = span.GetOffsetY();
667
668 if (auto as = span.TryToAnySpan(); as != nullptr) {
669 auto rect = TexgineRect::MakeXYWH(offsetX, offsetY, span.GetWidth(), span.GetHeight());
670 spanBoxes.push_back({.rect = rect, .direction = TextDirection::LTR});
671 }
672
673 if (auto ts = span.TryToTextSpan(); ts != nullptr) {
674 double top = *(ts->tmetrics_.fAscent_);
675 double height = *(ts->tmetrics_.fDescent_) - *(ts->tmetrics_.fAscent_);
676
677 std::vector<TextRect> boxes;
678 double width = 0.0;
679 for (const auto &gw : ts->glyphWidths_) {
680 auto rect = TexgineRect::MakeXYWH(offsetX + width, offsetY + top, gw, height);
681 boxes.push_back({.rect = rect, .direction = TextDirection::LTR});
682 width += gw;
683 }
684
685 spanBoxes.insert(spanBoxes.end(), boxes.begin(), boxes.end());
686 }
687
688 if (calcResult.need) {
689 for (const auto &box : spanBoxes) {
690 *(box.rect.fTop_) = baseline - calcResult.ascent;
691 *(box.rect.fBottom_) = baseline + calcResult.descent;
692 }
693 }
694
695 lineBoxes.insert(lineBoxes.end(), spanBoxes.begin(), spanBoxes.end());
696 }
697 }
698
MergeRects(const std::vector<TextRect> & boxes,Boundary boundary) const699 std::vector<TextRect> TypographyImpl::MergeRects(const std::vector<TextRect> &boxes, Boundary boundary) const
700 {
701 if (boundary.leftIndex > boxes.size()) {
702 return {};
703 }
704
705 if (boundary.rightIndex > boxes.size()) {
706 boundary.rightIndex = boxes.size();
707 }
708
709 std::optional<TextRect> pre = std::nullopt;
710 std::vector<TextRect> rects;
711 for (auto it = boxes.cbegin() + boundary.leftIndex; it < boxes.cbegin() + boundary.rightIndex; it++) {
712 const auto &rect = *it;
713 if (!pre.has_value()) {
714 pre = rect;
715 continue;
716 }
717
718 if (*pre->rect.fTop_ == *rect.rect.fTop_ && *pre->rect.fBottom_ == *rect.rect.fBottom_ &&
719 std::fabs(*pre->rect.fRight_ - *rect.rect.fLeft_) < MINDEV) {
720 *pre->rect.fRight_ = *rect.rect.fRight_;
721 } else {
722 rects.push_back(pre.value());
723 pre = rect;
724 }
725 }
726
727 if (pre.has_value()) {
728 rects.push_back(pre.value());
729 }
730
731 return rects;
732 }
733
GetTextRectsOfPlaceholders() const734 std::vector<TextRect> TypographyImpl::GetTextRectsOfPlaceholders() const
735 {
736 std::vector<TextRect> rects;
737 for (const auto &line : lineMetrics_) {
738 for (const auto &span : line.lineSpans) {
739 if (span == nullptr || span.TryToTextSpan() != nullptr) {
740 continue;
741 }
742
743 auto as = span.TryToAnySpan();
744 auto rect = TexgineRect::MakeXYWH(span.GetOffsetX(), span.GetOffsetY(), span.GetWidth(), span.GetHeight());
745 rects.push_back({.rect = rect, .direction = TextDirection::LTR});
746 }
747 }
748
749 return rects;
750 }
751
ApplyAlignment()752 void TypographyImpl::ApplyAlignment()
753 {
754 TextAlign align_ = typographyStyle_.GetEquivalentAlign();
755 for (auto &line : lineMetrics_) {
756 double typographyOffsetX = 0.0;
757 if (TextAlign::RIGHT == align_ || (TextAlign::JUSTIFY == align_ &&
758 TextDirection::RTL == typographyStyle_.direction)) {
759 typographyOffsetX = maxWidth_ - line.width;
760 } else if (TextAlign::CENTER == align_) {
761 typographyOffsetX = (maxWidth_ - line.width) * HALF;
762 }
763
764 for (auto &span : line.lineSpans) {
765 span.AdjustOffsetX(typographyOffsetX);
766 }
767 line.indent = typographyOffsetX;
768 }
769 }
770 } // namespace TextEngine
771 } // namespace Rosen
772 } // namespace OHOS
773