• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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