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