• 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 "font_collection.h"
23 #include "shaper.h"
24 #include "texgine/any_span.h"
25 #include "texgine_exception.h"
26 #include "text_span.h"
27 #include "texgine/typography_types.h"
28 #include "texgine/utils/exlog.h"
29 #ifdef LOGGER_ENABLE_SCOPE
30 #include "texgine/utils/trace.h"
31 #endif
32 #include "word_breaker.h"
33 
34 namespace OHOS {
35 namespace Rosen {
36 namespace TextEngine {
37 #define MAXWIDTH 1e9
38 #define HALF(a) ((a) / 2)
39 #define MINDEV 1e-3
40 #define MAX_INT_VALUE 0x7FFFFFFF
41 #define SUCCESSED 0
42 #define FAILED 1
43 
AddSpanAndUpdateMetrics(const VariantSpan & span)44 void LineMetrics::AddSpanAndUpdateMetrics(const VariantSpan &span)
45 {
46     lineSpans.push_back(span);
47     width += span.GetWidth();
48 }
49 
Boundary(size_t left,size_t right)50 Boundary::Boundary(size_t left, size_t right)
51 {
52     leftIndex = left;
53     rightIndex = right;
54 }
55 
TypographyImpl(TypographyStyle & ys,std::vector<VariantSpan> & spans,std::shared_ptr<FontProviders> providers)56 TypographyImpl::TypographyImpl(TypographyStyle &ys, std::vector<VariantSpan> &spans,
57     std::shared_ptr<FontProviders> providers
58     ): typographyStyle_(std::move(ys)), spans_(std::move(spans)), fontProviders_(providers)
59 {
60 }
61 
GetAlphabeticBaseline() const62 double TypographyImpl::GetAlphabeticBaseline() const
63 {
64     if (lineMaxCoveredAscent_.empty()) {
65         return 0.0;
66     }
67 
68     return lineMaxCoveredAscent_[0];
69 }
70 
GetIdeographicBaseline() const71 double TypographyImpl::GetIdeographicBaseline() const
72 {
73     if (lineMaxCoveredAscent_.empty() || lineMaxCoveredDescent_.empty()) {
74         return 0.0;
75     }
76 
77     return lineMaxCoveredAscent_[0] + lineMaxCoveredDescent_[0];
78 }
79 
DidExceedMaxLines() const80 bool TypographyImpl::DidExceedMaxLines() const
81 {
82     return didExceedMaxLines_;
83 }
84 
GetHeight() const85 double TypographyImpl::GetHeight() const
86 {
87     return height_;
88 }
89 
GetMaxWidth() const90 double TypographyImpl::GetMaxWidth() const
91 {
92     return maxWidth_;
93 }
94 
GetActualWidth() const95 double TypographyImpl::GetActualWidth() const
96 {
97     return maxLineWidth_;
98 }
99 
GetMaxIntrinsicWidth() const100 double TypographyImpl::GetMaxIntrinsicWidth() const
101 {
102     return maxIntrinsicWidth_;
103 }
104 
GetMinIntrinsicWidth() const105 double TypographyImpl::GetMinIntrinsicWidth() const
106 {
107     return minIntrinsicWidth_;
108 }
109 
GetLineCount() const110 int TypographyImpl::GetLineCount() const
111 {
112     return lineMetrics_.size();
113 }
114 
SetIndents(const std::vector<float> & indents)115 void TypographyImpl::SetIndents(const std::vector<float> &indents)
116 {
117     indents_ = indents;
118 }
119 
FindGlyphTargetLine(double y) const120 size_t TypographyImpl::FindGlyphTargetLine(double y) const
121 {
122     int targetLine = 0;
123     for (auto i = 0; i < static_cast<int>(lineMetrics_.size()); i++) {
124         if (y < baselines_[i] + lineMaxCoveredDescent_[i]) {
125             break;
126         }
127         targetLine = i + 1;
128     }
129     return targetLine;
130 }
131 
FindGlyphTargetIndex(size_t line,double x,double & offsetX,std::vector<double> & widths) const132 size_t TypographyImpl::FindGlyphTargetIndex(size_t line,
133     double x, double &offsetX, std::vector<double> &widths) const
134 {
135     // gather widths
136     widths = { lineMetrics_[line].indent };
137     for (const auto &vs : lineMetrics_[line].lineSpans) {
138         if (vs == nullptr) {
139             continue;
140         }
141 
142         auto ws = vs.GetGlyphWidths();
143         if (vs.GetJustifyGap() > 0) {
144             widths.insert(widths.end(), -vs.GetJustifyGap());
145         }
146         widths.insert(widths.end(), ws.begin(), ws.end());
147     }
148 
149     offsetX = 0;
150     size_t targetIndex = 0;
151     for (const auto &width : widths) {
152         if (x < offsetX + fabs(width) * typographyStyle_.textSplitRatio) {
153             break;
154         }
155 
156         if (width >= 0) {
157             targetIndex++;
158         }
159         offsetX += fabs(width);
160     }
161     return targetIndex;
162 }
163 
GetGlyphIndexByCoordinate(double x,double y) const164 IndexAndAffinity TypographyImpl::GetGlyphIndexByCoordinate(double x, double y) const
165 {
166     std::stringstream ss;
167     ss << "GetGlyphPositionAtCoordinate(" << x << "," << y << ")";
168     LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), ss.str());
169 
170     // process y < 0
171     if (fabs(height_) < DBL_EPSILON) {
172         return {0, Affinity::NEXT};
173     }
174 
175     // find targetLine
176     int targetLine = static_cast<int>(FindGlyphTargetLine(y));
177     if (targetLine == static_cast<int>(lineMetrics_.size())) {
178         // process y more than typography, (lineMetrics_.size() - 1) is the last line
179         targetLine = static_cast<int>(lineMetrics_.size() - 1);
180     }
181 
182     // count glyph before targetLine
183     size_t count = 0;
184     for (auto i = 0; i < targetLine; i++) {
185         for (const auto &span : lineMetrics_[i].lineSpans) {
186             count += span.GetNumberOfChar();
187         }
188     }
189 
190     // find targetIndex
191     double offsetX = 0;
192     std::vector<double> widths;
193     auto targetIndex = FindGlyphTargetIndex(targetLine, x, offsetX, widths);
194     count += targetIndex;
195 
196     // process first line left part
197     if (targetIndex == 0 && targetLine == 0) {
198         return {0, Affinity::NEXT};
199     }
200 
201     auto affinity = Affinity::PREV;
202     if (targetIndex == widths.size()) {
203         // process right part, (count - 1) is the index of last chargroup
204         return {count - 1, affinity};
205     }
206 
207     // calc affinity
208     if (targetIndex > 0 && targetIndex < widths.size()) {
209         auto mid = offsetX + fabs(widths[targetIndex]) * typographyStyle_.textSplitRatio;
210         if (x < mid) {
211             count--;
212             affinity = Affinity::NEXT;
213         } else {
214             affinity = Affinity::PREV;
215         }
216     }
217 
218     return {count, affinity};
219 }
220 
ComputeWordBoundary() const221 void TypographyImpl::ComputeWordBoundary() const
222 {
223     if (!boundariesCache_.empty()) {
224         return;
225     }
226 
227     for (const auto &span : spans_) {
228         auto offset = boundariesCache_.empty() ? 0 : boundariesCache_.back().rightIndex;
229         if (span.TryToAnySpan()) {
230             boundariesCache_.emplace_back(offset, offset + 1);
231             continue;
232         }
233 
234         if (const auto &ts = span.TryToTextSpan(); ts != nullptr) {
235             WordBreaker wb;
236             wb.SetLocale(icu::Locale::createFromName(span.GetTextStyle().locale.c_str()));
237             wb.SetRange(0, ts->u16vect_.size());
238             auto boundaries = wb.GetBoundary(ts->u16vect_, true);
239             if (boundaries.empty()) {
240                 continue;
241             }
242             for (const auto &[left, right] : boundaries) {
243                 boundariesCache_.emplace_back(left + offset, right + offset);
244             }
245         }
246     }
247 }
248 
GetWordBoundaryByIndex(size_t index) const249 Boundary TypographyImpl::GetWordBoundaryByIndex(size_t index) const
250 {
251     ComputeWordBoundary();
252     if (boundariesCache_.empty()) {
253         return {0, 0};
254     }
255 
256     for (const auto &boundary : boundariesCache_) {
257         if (boundary.leftIndex <= index && index < boundary.rightIndex) {
258             return boundary;
259         }
260     }
261 
262     auto right = boundariesCache_.back().rightIndex;
263     return {right, right};
264 }
265 
GetActualTextRange(int lineNumber,bool includeSpaces) const266 Boundary TypographyImpl::GetActualTextRange(int lineNumber, bool includeSpaces) const
267 {
268     return {0, 0};
269 }
270 
GetLineHeight(int lineNumber)271 double TypographyImpl::GetLineHeight(int lineNumber)
272 {
273     if (lineNumber >= 0 && lineNumber < static_cast<int>(lineMetrics_.size())) {
274         return lineMetrics_[lineNumber].GetMaxHeight();
275     } else {
276         return 0.0;
277     }
278 }
279 
GetLineWidth(int lineNumber)280 double TypographyImpl::GetLineWidth(int lineNumber)
281 {
282     if (lineNumber >= 0 && lineNumber < static_cast<int>(lineMetrics_.size())) {
283         return lineMetrics_[lineNumber].width;
284     } else {
285         return 0.0;
286     }
287 }
288 
Layout(double maxWidth)289 void TypographyImpl::Layout(double maxWidth)
290 {
291     boundariesCache_ = {};
292     try {
293 #ifdef LOGGER_ENABLE_SCOPE
294         ScopedTrace scope("TypographyImpl::Layout");
295 #endif
296         LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "TypographyImpl::Layout");
297         LOGEX_FUNC_LINE_DEBUG() << "Layout maxWidth: " << maxWidth << ", spans.size(): " << spans_.size();
298         maxWidth_ = floor(maxWidth);
299         auto isEmptySpans = spans_.empty();
300         if (isEmptySpans) {
301             LOGEX_FUNC_LINE(ERROR) << "Empty spans";
302             std::vector<uint16_t> text{u'\n'};
303             VariantSpan vs = TextSpan::MakeFromText(text);
304             vs.SetTextStyle(typographyStyle_.ConvertToTextStyle());
305             spans_.push_back(vs);
306         }
307 
308         Shaper shaper;
309         shaper.SetIndents(indents_);
310         lineMetrics_ = shaper.DoShape(spans_, typographyStyle_, fontProviders_, maxWidth_);
311         if (isEmptySpans) {
312             lineMetrics_.pop_back();
313         }
314         if (lineMetrics_.size() == 0) {
315             LOGEX_FUNC_LINE_DEBUG() << "Shape failed";
316             return;
317         }
318 
319         didExceedMaxLines_ = shaper.DidExceedMaxLines();
320         maxIntrinsicWidth_ = shaper.GetMaxIntrinsicWidth();
321         minIntrinsicWidth_ = shaper.GetMinIntrinsicWidth();
322 
323         auto ret = ComputeStrut();
324         if (ret) {
325             LOGEX_FUNC_LINE(ERROR) << "ComputeStrut failed";
326             return;
327         }
328 
329         ret = UpdateMetrics();
330         if (ret) {
331             LOGEX_FUNC_LINE(ERROR) << "UpdateMetrics failed";
332             return;
333         }
334 
335         DoLayout();
336         ApplyAlignment();
337     } catch (struct TexgineException &e) {
338         LOGEX_FUNC_LINE(ERROR) << "catch exception: " << e.message;
339     }
340 }
341 
UpdateMetrics()342 int TypographyImpl::UpdateMetrics()
343 {
344     LOGSCOPED(sl, LOGEX_FUNC_LINE_DEBUG(), "UpdateMetrics");
345     baselines_ = {};
346     lineMaxAscent_ = {};
347     lineMaxCoveredAscent_ = {};
348     lineMaxCoveredDescent_ = {};
349     yOffsets_ = {};
350     height_ = 0.0;
351     descent_ = 0.0;
352     double prevMaxDescent = 0.0;
353     double yOffset = 0.0;
354 
355     for (auto i = 0; i < static_cast<int>(lineMetrics_.size()); i++) {
356         lineMaxAscent_.push_back(strut_.ascent);
357         lineMaxCoveredAscent_.push_back(strut_.ascent + strut_.halfLeading);
358         lineMaxCoveredDescent_.push_back(strut_.descent + strut_.halfLeading);
359 
360         for (auto &span : lineMetrics_[i].lineSpans) {
361             if (span == nullptr) {
362                 LOGEX_FUNC_LINE(ERROR) << "span is nullptr";
363                 return FAILED;
364             }
365 
366             double coveredAscent = 0;
367             auto ret = UpdateSpanMetrics(span, coveredAscent);
368             if (ret) {
369                 LOGEX_FUNC_LINE(ERROR) << "UpdateMerics is failed";
370                 return FAILED;
371             }
372 
373             if (auto as = span.TryToAnySpan(); as != nullptr) {
374                 span.AdjustOffsetY(-ceil(coveredAscent));
375             }
376         }
377 
378         height_ += ceil(lineMaxCoveredAscent_.back() + lineMaxCoveredDescent_.back());
379         baselines_.push_back(height_ - lineMaxCoveredDescent_.back());
380         yOffset += ceil(lineMaxCoveredAscent_.back() + prevMaxDescent);
381         yOffsets_.push_back(yOffset);
382         prevMaxDescent = lineMaxCoveredDescent_.back();
383         LOGEX_FUNC_LINE_DEBUG() << "[" << i << "] ascent: " << lineMaxAscent_.back() <<
384             ", coveredAscent: " << lineMaxCoveredAscent_.back() <<
385             ", coveredDescent: " << lineMaxCoveredDescent_.back();
386     }
387 
388     return SUCCESSED;
389 }
390 
DoLayout()391 void TypographyImpl::DoLayout()
392 {
393     maxLineWidth_ = 0.0;
394     bool needMerge = false;
395     int size = static_cast<int>(lineMetrics_.size());
396     if (size > 1) {
397         needMerge = !lineMetrics_[size - 2].lineSpans.back().IsHardBreak() &&
398             lineMetrics_[size - 1].lineSpans.front().IsHardBreak();
399     }
400 
401     if (needMerge) {
402         lineMetrics_[size - 2].lineSpans.push_back(lineMetrics_[size - 1].lineSpans.front());
403         lineMetrics_[size - 1].lineSpans.erase(lineMetrics_[size - 1].lineSpans.begin());
404     }
405 
406     for (auto i = 0; i < static_cast<int>(lineMetrics_.size()); i++) {
407         std::vector<VariantSpan> groupSpans;
408         double offsetX = 0;
409         int index = 0;
410         int preIndex = -1; // Init preIndex to -1
411         for (auto &vs : lineMetrics_[i].lineSpans) {
412             vs.AdjustOffsetY(yOffsets_[i]);
413             vs.AdjustOffsetX(offsetX + HALF(vs.GetTextStyle().letterSpacing));
414             offsetX += vs.GetWidth();
415             lineMetrics_[i].width = offsetX;
416             ComputeRoundRect(vs, index, preIndex, lineMetrics_[i], groupSpans);
417         }
418         maxLineWidth_ = std::max(maxLineWidth_, lineMetrics_[i].width);
419     }
420 }
421 
ComputeRoundRect(VariantSpan & span,int & index,int & preIndex,LineMetrics & metric,std::vector<VariantSpan> & groupSpans)422 void TypographyImpl::ComputeRoundRect(VariantSpan& span, int& index, int& preIndex, LineMetrics& metric,
423     std::vector<VariantSpan>& groupSpans)
424 {
425     bool leftRound = false;
426     bool rightRound = false;
427     if (span.HasBackgroundRect()) {
428         int lineSpanCount = static_cast<int>(metric.lineSpans.size());
429         int styleId = span.GetTextStyle().styleId;
430         // index - 1 is previous index, -1 is the invalid styleId
431         int preStyleId = index == 0 ? -1 : metric.lineSpans[index - 1].GetTextStyle().styleId;
432         // lineSpanCount - 1 is the last span index, index + 1 is next span index, -1 is the invalid styleId
433         int nextStyleId = index == lineSpanCount - 1 ? -1 : metric.lineSpans[index + 1].GetTextStyle().styleId;
434         // index - preIndex > 1 means the left span has no background rect
435         leftRound = (preIndex < 0 || index - preIndex > 1 || preStyleId != styleId);
436         // lineSpanCount - 1 is the last span index, index + 1 is next span index
437         rightRound = (index == lineSpanCount - 1 || !metric.lineSpans[index + 1].HasBackgroundRect() ||
438             nextStyleId != styleId);
439         preIndex = index;
440         groupSpans.push_back(span);
441     } else if (!groupSpans.empty()) {
442         groupSpans.erase(groupSpans.begin(), groupSpans.end());
443     }
444     if (leftRound && rightRound) {
445         span.SetRoundRectType(RoundRectType::ALL);
446     } else if (leftRound) {
447         span.SetRoundRectType(RoundRectType::LEFT_ONLY);
448     } else if (rightRound) {
449         span.SetRoundRectType(RoundRectType::RIGHT_ONLY);
450     } else {
451         span.SetRoundRectType(RoundRectType::NONE);
452     }
453     index++;
454 
455     if (rightRound && !groupSpans.empty()) {
456         double maxRoundRectRadius = MAX_INT_VALUE;
457         double minTop = MAX_INT_VALUE;
458         double maxBottom = 0;
459         for (const auto &gSpan : groupSpans) {
460             maxRoundRectRadius = std::fmin(std::fmin(gSpan.GetWidth(), gSpan.GetHeight()), maxRoundRectRadius);
461             minTop = std::fmin(minTop, gSpan.GetTop());
462             maxBottom = std::fmax(maxBottom, gSpan.GetBottom());
463         }
464         for (auto &gSpan : groupSpans) {
465             gSpan.SetMaxRoundRectRadius(maxRoundRectRadius);
466             gSpan.SetTopInGroup(minTop - gSpan.GetOffsetY());
467             gSpan.SetBottomInGroup(maxBottom - gSpan.GetOffsetY());
468         }
469         groupSpans.erase(groupSpans.begin(), groupSpans.end());
470     }
471 }
472 
ComputeStrut()473 int TypographyImpl::ComputeStrut()
474 {
475     strut_ = {};
476 
477     bool strutValid = typographyStyle_.useLineStyle && typographyStyle_.lineStyle.fontSize >= 0;
478     if (!strutValid) {
479         return SUCCESSED;
480     }
481 
482     auto fontCollection = fontProviders_->GenerateFontCollection(
483         typographyStyle_.lineStyle.fontFamilies);
484     if (fontCollection == nullptr) {
485         LOGEX_FUNC_LINE(ERROR) << "fontCollection is null";
486         return FAILED;
487     }
488 
489     FontStyles style(typographyStyle_.lineStyle.fontWeight, typographyStyle_.lineStyle.fontStyle);
490     auto typeface = fontCollection->GetTypefaceForFontStyles(style, {}, {});
491     if (typeface == nullptr) {
492         LOGEX_FUNC_LINE(ERROR) << "seek typeface failed";
493         return FAILED;
494     }
495 
496     std::shared_ptr<TexgineFontMetrics> strutMetrics = std::make_shared<TexgineFontMetrics>();
497     TexgineFont font;
498     font.SetTypeface(typeface->Get());
499     font.SetSize(typographyStyle_.lineStyle.fontSize);
500     font.GetMetrics(strutMetrics);
501 
502     double strutLeading = typographyStyle_.lineStyle.spacingScale.value_or(0) * typographyStyle_.lineStyle.fontSize;
503     auto leading = strutLeading;
504     if (typographyStyle_.lineStyle.heightOnly) {
505         double metricsHeight = -*strutMetrics->fAscent_ + *strutMetrics->fDescent_;
506         if (fabs(metricsHeight) < DBL_EPSILON) {
507             LOGEX_FUNC_LINE(ERROR) << "strutMetrics is error";
508             return FAILED;
509         }
510 
511         double scale = typographyStyle_.lineStyle.heightScale * typographyStyle_.lineStyle.fontSize;
512         strut_.ascent = (-(*strutMetrics->fAscent_) / metricsHeight) * scale;
513         strut_.descent = (*strutMetrics->fDescent_ / metricsHeight) * scale;
514     } else {
515         strut_.ascent = -(*strutMetrics->fAscent_);
516         strut_.descent = *strutMetrics->fDescent_;
517         leading = fabs(leading) < DBL_EPSILON ? *strutMetrics->fLeading_ : strutLeading;
518     }
519     strut_.halfLeading = HALF(leading);
520     return SUCCESSED;
521 }
522 
UpdateSpanMetrics(VariantSpan & span,double & coveredAscent)523 int TypographyImpl::UpdateSpanMetrics(VariantSpan &span, double &coveredAscent)
524 {
525     auto style = span.GetTextStyle();
526     std::shared_ptr<TexgineFontMetrics> metrics = nullptr;
527     if (auto ts = span.TryToTextSpan(); ts != nullptr) {
528         metrics = ts->tmetrics_;
529         descent_ = *metrics->fDescent_;
530     } else {
531         metrics = std::make_shared<TexgineFontMetrics>();
532         auto as = span.TryToAnySpan();
533         auto families = style.fontFamilies;
534         if (families.empty()) {
535             families = typographyStyle_.fontFamilies;
536         }
537         auto fontCollection = fontProviders_->GenerateFontCollection(families);
538         if (fontCollection == nullptr) {
539             LOGEX_FUNC_LINE(ERROR) << "fontCollection is nullptr";
540             return FAILED;
541         }
542 
543         FontStyles fs(style.fontWeight, style.fontStyle);
544         // 0xFFFC is a placeholder, use it to get typeface when text is empty.
545         auto typeface = fontCollection->GetTypefaceForChar(0xFFFC, fs, "Latn", style.locale);
546         if (typeface == nullptr) {
547             typeface = fontCollection->GetTypefaceForFontStyles(fs, "Latn", style.locale);
548         }
549         if (typeface == nullptr) {
550             LOGEX_FUNC_LINE(ERROR) << "typeface is nullptr";
551             return FAILED;
552         }
553 
554         TexgineFont font;
555         font.SetTypeface(typeface->Get());
556         font.SetSize(style.fontSize);
557         font.GetMetrics(metrics);
558         descent_ = std::max(*metrics->fDescent_, descent_);
559     }
560     if (DoUpdateSpanMetrics(span, metrics, style, coveredAscent)) {
561         LOGEX_FUNC_LINE(ERROR) << "DoUpdateSpanMetrics is error";
562         return FAILED;
563     }
564 
565     return SUCCESSED;
566 }
567 
DoUpdateSpanMetrics(const VariantSpan & span,const std::shared_ptr<TexgineFontMetrics> metrics,const TextStyle & style,double & coveredAscent)568 int TypographyImpl::DoUpdateSpanMetrics(const VariantSpan &span, const std::shared_ptr<TexgineFontMetrics> metrics,
569     const TextStyle &style, double &coveredAscent)
570 {
571     bool onlyUseStrut = typographyStyle_.useLineStyle;
572     onlyUseStrut = onlyUseStrut && (typographyStyle_.lineStyle.fontSize >= 0);
573     onlyUseStrut = onlyUseStrut && typographyStyle_.lineStyle.only;
574     double ascent = -*metrics->fAscent_;
575     if (!onlyUseStrut) {
576         double coveredDescent = 0;
577         if (style.heightOnly) {
578             double metricsHeight = -*metrics->fAscent_ + descent_;
579             if (fabs(metricsHeight) < DBL_EPSILON) {
580                 LOGEX_FUNC_LINE(ERROR) << "metrics is error";
581                 return FAILED;
582             }
583 
584             coveredAscent = (-*metrics->fAscent_ / metricsHeight) * style.heightScale * style.fontSize;
585             coveredDescent = (*metrics->fDescent_ / metricsHeight) * style.heightScale * style.fontSize;
586         } else {
587             coveredAscent = (-*metrics->fAscent_ + HALF(*metrics->fLeading_));
588             coveredDescent = (*metrics->fDescent_ + HALF(*metrics->fLeading_));
589         }
590         if (auto as = span.TryToAnySpan(); as != nullptr) {
591             UpadateAnySpanMetrics(as, coveredAscent, coveredDescent);
592             ascent = coveredAscent;
593         }
594         if (style.halfLeading) {
595             double height = -*metrics->fAscent_ + *metrics->fDescent_;
596             double blobHeight = style.heightOnly ? style.heightScale * style.fontSize : height + *metrics->fLeading_;
597             double leading = blobHeight - height;
598             double availableVspace = blobHeight - leading;
599             double halfLeading = HALF(leading);
600             coveredAscent = -*metrics->fAscent_ / height * availableVspace + halfLeading;
601             coveredDescent = *metrics->fDescent_ / height * availableVspace + halfLeading;
602         }
603         lineMaxCoveredAscent_.back() = std::max(lineMaxCoveredAscent_.back(), coveredAscent);
604         lineMaxCoveredDescent_.back() = std::max(lineMaxCoveredDescent_.back(), coveredDescent);
605     }
606     lineMaxAscent_.back() = std::max(lineMaxAscent_.back(), ascent);
607     return SUCCESSED;
608 }
609 
UpadateAnySpanMetrics(std::shared_ptr<AnySpan> & span,double & coveredAscent,double & coveredDescent)610 void TypographyImpl::UpadateAnySpanMetrics(std::shared_ptr<AnySpan> &span, double &coveredAscent,
611     double &coveredDescent)
612 {
613     if (span == nullptr) {
614         throw TEXGINE_EXCEPTION(INVALID_ARGUMENT);
615     }
616 
617     double as = coveredAscent;
618     double de = coveredDescent;
619     double aj = span->GetBaseline() == TextBaseline::IDEOGRAPHIC ? HALF(-de) : 0;
620     double lo = span->GetLineOffset();
621     double he = span->GetHeight();
622 
623     using CalcAscentFunc = std::function<double()>;
624     std::map<AnySpanAlignment, CalcAscentFunc> calcMap = {
625         {AnySpanAlignment::OFFSET_AT_BASELINE, [&] { return aj + lo; }},
626         {AnySpanAlignment::ABOVE_BASELINE, [&] { return aj + he; }},
627         {AnySpanAlignment::BELOW_BASELINE, [&] { return -aj; }},
628         {AnySpanAlignment::TOP_OF_ROW_BOX, [&] { return as; }},
629         {AnySpanAlignment::BOTTOM_OF_ROW_BOX, [&] { return he - de; }},
630         {AnySpanAlignment::CENTER_OF_ROW_BOX, [&] { return HALF(as - de + he); }},
631     };
632 
633     coveredAscent = calcMap[span->GetAlignment()]();
634     coveredDescent = he - coveredAscent;
635 }
636 
Paint(TexgineCanvas & canvas,double offsetX,double offsetY)637 void TypographyImpl::Paint(TexgineCanvas &canvas, double offsetX, double offsetY)
638 {
639     uint64_t symbolId = 0;
640     for (auto &metric : lineMetrics_) {
641         for (auto &span : metric.lineSpans) {
642             if (animationFunc_) {
643                 symbolId++;
644                 span.SetSymbolId(symbolId);
645                 span.SetAnimation(animationFunc_);
646             }
647             span.Paint(canvas, offsetX + span.GetOffsetX(), offsetY + span.GetOffsetY());
648         }
649     }
650 }
651 
GetTextRectsByBoundary(Boundary boundary,TextRectHeightStyle heightStyle,TextRectWidthStyle widthStyle) const652 std::vector<TextRect> TypographyImpl::GetTextRectsByBoundary(Boundary boundary, TextRectHeightStyle heightStyle,
653     TextRectWidthStyle widthStyle) const
654 {
655     if (boundary.leftIndex > boundary.rightIndex || boundary.leftIndex < 0 || boundary.rightIndex < 0) {
656         LOGEX_FUNC_LINE_DEBUG() << "the box range is error";
657         return {};
658     }
659     std::vector<TextRect> totalBoxes;
660     for (auto i = 0; i < static_cast<int>(lineMetrics_.size()); i++) {
661         if (baselines_.empty() || lineMaxAscent_.empty() || lineMaxCoveredAscent_.empty() ||
662             lineMaxCoveredDescent_.empty()) {
663             return {};
664         }
665         std::vector<TextRect> lineBoxes;
666         auto baseline = baselines_[i];
667         auto as = lineMaxAscent_[i];
668         auto ca = lineMaxCoveredAscent_[i];
669         auto cd = lineMaxCoveredDescent_[i];
670         auto hl = ca - as;
671         auto fl = i == 0 ? 1 : 0;
672         auto ll = i == static_cast<int>(lineMetrics_.size()) - 1 ? 1 : 0;
673         constexpr auto tgh = TextRectHeightStyle::TIGHT;
674         constexpr auto ctb = TextRectHeightStyle::COVER_TOP_AND_BOTTOM;
675         constexpr auto chf = TextRectHeightStyle::COVER_HALF_TOP_AND_BOTTOM;
676         constexpr auto ctp = TextRectHeightStyle::COVER_TOP;
677         constexpr auto cbm = TextRectHeightStyle::COVER_BOTTOM;
678 
679         std::map<TextRectHeightStyle, std::function<struct CalcResult()>> calcMap = {
680             {tgh, [&] { return CalcResult{false}; }},
681             {ctb, [&] { return CalcResult{true, as, cd}; }},
682             {chf, [&] { return CalcResult{true, as + HALF(fl * hl), cd + HALF(ll * hl)}; }},
683             {ctp, [&] { return CalcResult{true, as + fl * hl, cd}; }},
684             {cbm, [&] { return CalcResult{true, as, cd + ll * hl}; }},
685             {TextRectHeightStyle::FOLLOW_BY_LINE_STYLE, [&] {
686                 if (typographyStyle_.useLineStyle && typographyStyle_.lineStyle.fontSize >= 0) {
687                     return CalcResult{true, strut_.ascent, strut_.descent};
688                 }
689                 return CalcResult{false};
690             }},
691         };
692 
693         const auto &result = calcMap[heightStyle]();
694         ComputeSpans(i, baseline, result, lineBoxes);
695         if (widthStyle == TextRectWidthStyle::MAX_WIDTH) {
696             if (lineBoxes.empty()) {
697                 LOGEX_FUNC_LINE(ERROR) << "rects is null";
698             } else {
699                 *lineBoxes.back().rect.fRight_ += maxLineWidth_ - lineMetrics_[i].width;
700             }
701         }
702         totalBoxes.insert(totalBoxes.end(), lineBoxes.begin(), lineBoxes.end());
703     }
704     return MergeRects(totalBoxes, boundary);
705 }
706 
ComputeSpans(int lineIndex,double baseline,const CalcResult & calcResult,std::vector<TextRect> & lineBoxes) const707 void TypographyImpl::ComputeSpans(int lineIndex, double baseline, const CalcResult &calcResult,
708     std::vector<TextRect> &lineBoxes) const
709 {
710     for (const auto &span : lineMetrics_[lineIndex].lineSpans) {
711         if (span == nullptr) {
712             continue;
713         }
714 
715         std::vector<TextRect> spanBoxes;
716         auto offsetX = span.GetOffsetX();
717         auto offsetY = span.GetOffsetY();
718 
719         if (auto as = span.TryToAnySpan(); as != nullptr) {
720             auto rect = TexgineRect::MakeXYWH(offsetX, offsetY, span.GetWidth(), span.GetHeight());
721             spanBoxes.push_back({.rect = rect, .direction = TextDirection::LTR});
722         }
723 
724         bool isJustify = typographyStyle_.GetEquivalentAlign() == TextAlign::JUSTIFY &&
725             lineIndex != static_cast<int>(lineMetrics_.size() - 1) &&
726             !lineMetrics_[lineIndex].lineSpans.back().IsHardBreak() &&
727             lineMetrics_[lineIndex].lineSpans.size() > 1;
728         double spanGapWidth = 0.0;
729         if (isJustify) {
730             spanGapWidth = (maxWidth_ - lineMetrics_[lineIndex].width) /
731                 (lineMetrics_[lineIndex].lineSpans.size() - 1);
732         }
733         if (auto ts = span.TryToTextSpan(); ts != nullptr) {
734             std::vector<TextRect> boxes = GenTextRects(ts, offsetX, offsetY, spanGapWidth);
735             spanBoxes.insert(spanBoxes.end(), boxes.begin(), boxes.end());
736         }
737 
738         if (calcResult.need) {
739             for (const auto &box : spanBoxes) {
740                 *(box.rect.fTop_) = baseline - calcResult.ascent;
741                 *(box.rect.fBottom_) = baseline + calcResult.descent;
742             }
743         }
744 
745         lineBoxes.insert(lineBoxes.end(), spanBoxes.begin(), spanBoxes.end());
746     }
747 }
748 
GenTextRects(std::shared_ptr<TextSpan> & ts,double offsetX,double offsetY,double spanGapWidth) const749 std::vector<TextRect> TypographyImpl::GenTextRects(std::shared_ptr<TextSpan> &ts, double offsetX, double offsetY,
750     double spanGapWidth) const
751 {
752     double top = *(ts->tmetrics_->fAscent_);
753     double height = *(ts->tmetrics_->fDescent_) - *(ts->tmetrics_->fAscent_);
754 
755     std::vector<TextRect> boxes;
756     double width = 0.0;
757     for (int i = 0; i < static_cast<int>(ts->glyphWidths_.size()); i++) {
758         auto cg = ts->cgs_.Get(i);
759         // If is emoji, don`t need process ligature, so set chars size to 1
760         int charsSize = cg.IsEmoji() ? 1 : static_cast<int>(cg.chars.size());
761         double spanWidth = ts->glyphWidths_[i] / charsSize;
762         if (i == static_cast<int>(ts->glyphWidths_.size() - 1)) {
763             spanWidth += spanGapWidth;
764         }
765         for (int j = 0; j < charsSize; j++) {
766             auto rect = TexgineRect::MakeXYWH(offsetX + width, offsetY + top, spanWidth, height);
767             boxes.push_back({.rect = rect, .direction = TextDirection::LTR});
768             width += ts->glyphWidths_[i] / charsSize;
769         }
770     }
771 
772     return boxes;
773 }
774 
MergeRects(const std::vector<TextRect> & boxes,Boundary boundary) const775 std::vector<TextRect> TypographyImpl::MergeRects(const std::vector<TextRect> &boxes, Boundary boundary) const
776 {
777     if (boxes.size() == 0) {
778         return {};
779     }
780 
781     if (boundary.leftIndex >= boxes.size()) {
782         return {};
783     } else if (boundary.rightIndex > boxes.size()) {
784         boundary.rightIndex = boxes.size();
785     }
786 
787     std::optional<TextRect> pre = std::nullopt;
788     std::vector<TextRect> rects;
789     for (auto it = boxes.cbegin() + boundary.leftIndex; it < boxes.cbegin() + boundary.rightIndex; it++) {
790         const auto &rect = *it;
791         if (!pre.has_value()) {
792             pre = rect;
793             continue;
794         }
795 
796         if (*pre->rect.fTop_ == *rect.rect.fTop_ && *pre->rect.fBottom_ == *rect.rect.fBottom_ &&
797             std::fabs(*pre->rect.fRight_ - *rect.rect.fLeft_) < MINDEV) {
798             *pre->rect.fRight_ = *rect.rect.fRight_;
799         } else {
800 #ifdef CROSS_PLATFORM
801             rects.push_back(pre.__get());
802 #else
803             rects.push_back(pre.value());
804 #endif
805             pre = rect;
806         }
807     }
808 
809     if (pre.has_value()) {
810 #ifdef CROSS_PLATFORM
811         rects.push_back(pre.__get());
812 #else
813         rects.push_back(pre.value());
814 #endif
815     }
816 
817     return rects;
818 }
819 
GetTextRectsOfPlaceholders() const820 std::vector<TextRect> TypographyImpl::GetTextRectsOfPlaceholders() const
821 {
822     std::vector<TextRect> rects;
823     for (const auto &line : lineMetrics_) {
824         for (const auto &span : line.lineSpans) {
825             if (span == nullptr || span.TryToTextSpan() != nullptr) {
826                 continue;
827             }
828 
829             auto as = span.TryToAnySpan();
830             auto rect = TexgineRect::MakeXYWH(span.GetOffsetX(), span.GetOffsetY(), span.GetWidth(), span.GetHeight());
831             rects.push_back({.rect = rect, .direction = TextDirection::LTR});
832         }
833     }
834 
835     return rects;
836 }
837 
ApplyAlignment()838 void TypographyImpl::ApplyAlignment()
839 {
840     TextAlign align_ = typographyStyle_.GetEquivalentAlign();
841     size_t lineIndex = 0;
842     for (auto &line : lineMetrics_) {
843         bool isJustify = false;
844         double spanGapWidth = 0.0;
845         double typographyOffsetX = line.indent;
846         if (TextAlign::RIGHT == align_ || (TextAlign::JUSTIFY == align_ &&
847             TextDirection::RTL == typographyStyle_.direction)) {
848             typographyOffsetX = maxWidth_ - line.width - line.indent;
849         } else if (TextAlign::CENTER == align_) {
850             if (typographyStyle_.direction == TextDirection::LTR) {
851                 typographyOffsetX = HALF(maxWidth_ - line.width) + line.indent;
852             } else if (typographyStyle_.direction == TextDirection::RTL) {
853                 typographyOffsetX = HALF(maxWidth_ - line.width) - line.indent;
854             }
855         } else {
856             // lineMetrics_.size() - 1 is last line index
857             isJustify = align_ == TextAlign::JUSTIFY && lineIndex != lineMetrics_.size() - 1 &&
858                 !line.lineSpans.back().IsHardBreak() && line.lineSpans.size() > 1;
859             if (isJustify) {
860                 // line.lineSpans.size() - 1 is gap count
861                 spanGapWidth = (maxWidth_ - line.width) / (line.lineSpans.size() - 1);
862             }
863         }
864 
865         size_t spanIndex = 0;
866         for (auto &span : line.lineSpans) {
867             span.AdjustOffsetX(typographyOffsetX + spanGapWidth * spanIndex);
868             span.SetJustifyGap(spanIndex > 0 && isJustify ? spanGapWidth : 0.0);
869             spanIndex++;
870         }
871         line.indent = typographyOffsetX;
872         lineIndex++;
873     }
874 }
875 } // namespace TextEngine
876 } // namespace Rosen
877 } // namespace OHOS
878