1/*
2 * Copyright (c) 2020-2022 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 "draw/draw_label.h"
17#include "common/typed_text.h"
18#include "draw/draw_utils.h"
19#include "engines/gfx/gfx_engine_manager.h"
20#include "font/ui_font.h"
21#include "font/ui_font_header.h"
22#include "gfx_utils/graphic_log.h"
23
24namespace OHOS {
25uint16_t DrawLabel::DrawTextOneLine(BufferInfo& gfxDstBuffer, const LabelLineInfo& labelLine, uint16_t& letterIndex)
26{
27    if (labelLine.text == nullptr) {
28        return 0;
29    }
30    UIFont* fontEngine = UIFont::GetInstance();
31    if (labelLine.direct == TEXT_DIRECT_RTL) {
32        labelLine.pos.x -= labelLine.offset.x;
33    } else {
34        labelLine.pos.x += labelLine.offset.x;
35    }
36
37    uint32_t i = 0;
38    uint16_t retOffsetY = 0; // ret value elipse offsetY
39    uint16_t offsetPosY = 0;
40    uint8_t maxLetterSize = GetLineMaxLetterSize(labelLine.text, labelLine.lineLength, labelLine.fontId,
41                                                 labelLine.fontSize, letterIndex, labelLine.spannableString);
42    GlyphNode glyphNode;
43    while (i < labelLine.lineLength) {
44        uint32_t letter = TypedText::GetUTF8Next(labelLine.text, i, i);
45        uint16_t fontId = labelLine.fontId;
46        uint8_t fontSize = labelLine.fontSize;
47#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE
48        TextStyle textStyle = TEXT_STYLE_NORMAL;
49        if (labelLine.textStyles) {
50            textStyle = labelLine.textStyles[letterIndex];
51        }
52#endif
53        bool haveLineBackgroundColor = false;
54        ColorType lineBackgroundColor;
55        bool havebackgroundColor = false;
56        ColorType backgroundColor;
57        ColorType foregroundColor = labelLine.style.textColor_;
58
59        if (labelLine.spannableString != nullptr && labelLine.spannableString->GetSpannable(letterIndex)) {
60            labelLine.spannableString->GetFontId(letterIndex, fontId);
61            labelLine.spannableString->GetFontSize(letterIndex, fontSize);
62            havebackgroundColor = labelLine.spannableString->GetBackgroundColor(letterIndex, backgroundColor);
63            labelLine.spannableString->GetForegroundColor(letterIndex, foregroundColor);
64#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE
65            labelLine.spannableString->GetTextStyle(letterIndex, textStyle);
66#endif
67            haveLineBackgroundColor =
68                labelLine.spannableString->GetLineBackgroundColor(letterIndex, lineBackgroundColor);
69        }
70        LabelLetterInfo letterInfo{labelLine.pos,
71                                   labelLine.mask,
72                                   foregroundColor,
73                                   labelLine.opaScale,
74                                   0,
75                                   0,
76                                   letter,
77                                   labelLine.direct,
78                                   fontId,
79                                   0,
80                                   fontSize,
81#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE
82                                   textStyle,
83#endif
84                                   labelLine.baseLine,
85                                   labelLine.style.letterSpace_,
86                                   labelLine.style.lineSpace_,
87                                   havebackgroundColor,
88                                   backgroundColor,
89                                   haveLineBackgroundColor,
90                                   lineBackgroundColor
91                                   };
92#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE
93        glyphNode.textStyle = letterInfo.textStyle;
94#endif
95        glyphNode.advance = 0;
96        uint8_t* fontMap = fontEngine->GetBitmap(letterInfo.letter, glyphNode, letterInfo.fontId, letterInfo.fontSize,
97                                                 letterInfo.shapingId);
98        if (fontMap != nullptr) {
99            uint8_t weight = fontEngine->GetFontWeight(glyphNode.fontId);
100            // 16: rgb565->16 rgba8888->32 font with rgba
101            if (weight >= 16) {
102                DrawUtils::GetInstance()->DrawColorLetter(gfxDstBuffer, letterInfo, fontMap,
103                                                          glyphNode, labelLine.lineHeight);
104            } else {
105                letterInfo.offsetY = labelLine.ellipsisOssetY == 0 ? offsetPosY : labelLine.ellipsisOssetY;
106                retOffsetY = offsetPosY;
107                DrawUtils::GetInstance()->DrawNormalLetter(gfxDstBuffer, letterInfo, fontMap, glyphNode, maxLetterSize);
108            }
109        }
110        if (labelLine.direct == TEXT_DIRECT_RTL) {
111            labelLine.pos.x -= (glyphNode.advance + labelLine.style.letterSpace_);
112        } else {
113            labelLine.pos.x += (glyphNode.advance + labelLine.style.letterSpace_);
114        }
115        letterIndex++;
116    }
117    return retOffsetY;
118}
119
120uint8_t DrawLabel::GetLineMaxLetterSize(const char* text, uint16_t lineLength, uint16_t fontId, uint8_t fontSize,
121                                        uint16_t letterIndex, SpannableString* spannableString)
122{
123    if (spannableString == nullptr) {
124        return fontSize;
125    }
126    uint32_t i = 0;
127    uint8_t maxLetterSize = fontSize;
128    while (i < lineLength) {
129        uint32_t unicode = TypedText::GetUTF8Next(text, i, i);
130        if (TypedText::IsColourWord(unicode, fontId, fontSize)) {
131            letterIndex++;
132            continue;
133        }
134        if (spannableString != nullptr && spannableString->GetSpannable(letterIndex)) {
135            uint8_t tempSize = fontSize;
136            spannableString->GetFontSize(letterIndex, tempSize);
137            if (tempSize > maxLetterSize) {
138                maxLetterSize = tempSize;
139            }
140        }
141        letterIndex++;
142    }
143    return maxLetterSize;
144}
145
146void DrawLabel::DrawArcText(BufferInfo& gfxDstBuffer,
147                            const Rect& mask,
148                            const char* text,
149                            const Point& arcCenter,
150                            uint16_t fontId,
151                            uint8_t fontSize,
152                            const ArcTextInfo arcTextInfo,
153                            const float changeAngle,
154                            TextOrientation orientation,
155                            const Style& style,
156                            OpacityType opaScale,
157                            bool compatibilityMode)
158{
159    if ((text == nullptr) || (arcTextInfo.lineStart == arcTextInfo.lineEnd) || (arcTextInfo.radius == 0)) {
160        GRAPHIC_LOGE("DrawLabel::DrawArcText invalid parameter\n");
161        return;
162    }
163    OpacityType opa = DrawUtils::GetMixOpacity(opaScale, style.textOpa_);
164    if (opa == OPA_TRANSPARENT) {
165        return;
166    }
167    uint16_t letterWidth;
168    UIFont* fontEngine = UIFont::GetInstance();
169
170    uint16_t letterHeight = fontEngine->GetHeight(fontId, fontSize);
171    uint32_t i = arcTextInfo.lineStart;
172    bool orientationFlag = (orientation == TextOrientation::INSIDE);
173    bool directFlag = (arcTextInfo.direct == TEXT_DIRECT_LTR);
174    bool xorFlag = !directFlag;
175    if (compatibilityMode) {
176        xorFlag = !((orientationFlag && directFlag) || (!orientationFlag && !directFlag));
177    }
178    float angle = directFlag ? (arcTextInfo.startAngle + changeAngle) : (arcTextInfo.startAngle - changeAngle);
179
180    float posX;
181    float posY;
182    float rotateAngle;
183    while (i < arcTextInfo.lineEnd) {
184        uint32_t tmp = i;
185        uint32_t letter = TypedText::GetUTF8Next(text, tmp, i);
186        if (letter == 0) {
187            continue;
188        }
189        if ((letter == '\r') || (letter == '\n')) {
190            break;
191        }
192        letterWidth = fontEngine->GetWidth(letter, fontId, fontSize, 0);
193        if (!DrawLabel::CalculateAngle(letterWidth, letterHeight, style.letterSpace_,
194                                       arcTextInfo, xorFlag, tmp, orientation,
195                                       posX, posY, rotateAngle, angle,
196                                       arcCenter, compatibilityMode)) {
197            continue;
198        }
199
200        ArcLetterInfo letterInfo;
201        letterInfo.InitData(fontId, fontSize, letter, { MATH_ROUND(posX), MATH_ROUND(posY) },
202            static_cast<int16_t>(rotateAngle), style.textColor_, opaScale, arcTextInfo.startAngle,
203            arcTextInfo.endAngle, angle, arcTextInfo.radius, compatibilityMode,
204            directFlag, orientationFlag, arcTextInfo.hasAnimator);
205
206        DrawLetterWithRotate(gfxDstBuffer, mask, letterInfo, posX, posY);
207    }
208}
209
210bool DrawLabel::CalculateAngle(uint16_t letterWidth,
211                               uint16_t letterHeight,
212                               int16_t letterSpace,
213                               const ArcTextInfo arcTextInfo,
214                               bool xorFlag,
215                               uint32_t index,
216                               TextOrientation orientation,
217                               float& posX,
218                               float& posY,
219                               float& rotateAngle,
220                               float& angle,
221                               const Point& arcCenter,
222                               bool compatibilityMode)
223{
224    const int DIVIDER_BY_TWO = 2;
225    if (compatibilityMode) {
226        if ((index == arcTextInfo.lineStart) && xorFlag) {
227            angle += TypedText::GetAngleForArcLen(static_cast<float>(letterWidth), letterHeight, arcTextInfo.radius,
228                                                  arcTextInfo.direct, orientation);
229        }
230        uint16_t arcLen = letterWidth + letterSpace;
231        if (arcLen == 0) {
232            return false;
233        }
234        float incrementAngle = TypedText::GetAngleForArcLen(static_cast<float>(arcLen), letterHeight,
235                                                            arcTextInfo.radius, arcTextInfo.direct, orientation);
236
237        rotateAngle = (orientation == TextOrientation::INSIDE) ? angle : (angle - SEMICIRCLE_IN_DEGREE);
238
239        // 2: half
240        float fineTuningAngle = incrementAngle * (static_cast<float>(letterWidth) / (DIVIDER_BY_TWO * arcLen));
241        rotateAngle += (xorFlag ? -fineTuningAngle : fineTuningAngle);
242        TypedText::GetArcLetterPos(arcCenter, arcTextInfo.radius, angle, posX, posY);
243        angle += incrementAngle;
244    } else {
245        float incrementAngle = TypedText::GetAngleForArcLen(letterWidth, letterSpace, arcTextInfo.radius);
246        if (incrementAngle >= -EPSINON && incrementAngle <= EPSINON) {
247            return false;
248        }
249
250        float fineTuningAngle = incrementAngle / DIVIDER_BY_TWO;
251        rotateAngle = xorFlag ? (angle - SEMICIRCLE_IN_DEGREE - fineTuningAngle) : (angle + fineTuningAngle);
252        TypedText::GetArcLetterPos(arcCenter, arcTextInfo.radius, angle, posX, posY);
253        angle = xorFlag ? (angle - incrementAngle) : (angle + incrementAngle);
254    }
255
256    return true;
257}
258
259void DrawLabel::DrawLetterWithRotate(BufferInfo& gfxDstBuffer,
260                                     const Rect& mask,
261                                     const ArcLetterInfo& letterInfo,
262                                     float posX,
263                                     float posY)
264{
265    UIFont* fontEngine = UIFont::GetInstance();
266    FontHeader head;
267    GlyphNode node;
268#if defined(ENABLE_TEXT_STYLE) && ENABLE_TEXT_STYLE
269    node.textStyle = TEXT_STYLE_NORMAL;
270#endif
271    if (fontEngine->GetFontHeader(head, letterInfo.fontId, letterInfo.fontSize) != 0) {
272        return;
273    }
274
275    const uint8_t* fontMap = fontEngine->GetBitmap(letterInfo.letter, node,
276        letterInfo.fontId, letterInfo.fontSize, 0);
277    if (fontMap == nullptr) {
278        return;
279    }
280    uint8_t fontWeight = fontEngine->GetFontWeight(letterInfo.fontId);
281    ColorMode colorMode = fontEngine->GetColorType(letterInfo.fontId);
282
283    int16_t offset = letterInfo.compatibilityMode ? head.ascender : 0;
284    Rect rectLetter;
285    rectLetter.Resize(node.cols, node.rows);
286    TransformMap transMap(rectLetter);
287    // Avoiding errors caused by rounding calculations
288    transMap.Translate(Vector2<float>(posX + node.left, posY + offset - node.top));
289    transMap.Rotate(letterInfo.rotateAngle, Vector2<float>(posX, posY));
290
291    TransformDataInfo letterTranDataInfo = {ImageHeader{colorMode, 0, 0, 0, node.cols, node.rows}, fontMap, fontWeight,
292                                            BlurLevel::LEVEL0, TransformAlgorithm::BILINEAR};
293
294    uint8_t* buffer = nullptr;
295    if (letterInfo.hasAnimator) {
296        bool inRange = DrawLabel::CalculatedTransformDataInfo(&buffer, letterTranDataInfo, letterInfo);
297        if (inRange == false) {
298            if (buffer != nullptr) {
299                UIFree(buffer);
300                buffer = nullptr;
301            }
302            return;
303        }
304    }
305
306    BaseGfxEngine::GetInstance()->DrawTransform(gfxDstBuffer, mask, Point { 0, 0 }, letterInfo.color,
307        letterInfo.opaScale, transMap, letterTranDataInfo);
308    if (buffer != nullptr) {
309        UIFree(buffer);
310        buffer = nullptr;
311    }
312}
313
314bool DrawLabel::CalculatedClipAngle(const ArcLetterInfo& letterInfo, float& angle)
315{
316    if (letterInfo.directFlag) {
317        if ((letterInfo.compatibilityMode && letterInfo.orientationFlag) || !letterInfo.compatibilityMode) {
318            if (letterInfo.currentAngle > letterInfo.endAngle) {
319                angle = letterInfo.currentAngle - letterInfo.endAngle;
320            } else if (letterInfo.currentAngle > letterInfo.startAngle) {
321                angle = letterInfo.currentAngle - letterInfo.startAngle;
322            } else {
323                return false;
324            }
325        } else {
326            if (letterInfo.currentAngle > letterInfo.endAngle) {
327                angle = letterInfo.currentAngle - letterInfo.endAngle;
328            } else if (letterInfo.currentAngle > letterInfo.startAngle) {
329                angle = letterInfo.currentAngle - letterInfo.startAngle;
330            } else {
331                return false;
332            }
333        }
334    } else {
335        if (letterInfo.compatibilityMode && letterInfo.orientationFlag) {
336            if (letterInfo.currentAngle < letterInfo.endAngle) {
337                angle = letterInfo.endAngle - letterInfo.currentAngle;
338            } else if (letterInfo.currentAngle < letterInfo.startAngle) {
339                angle = letterInfo.startAngle - letterInfo.currentAngle;
340            } else {
341                return false;
342            }
343        } else if ((letterInfo.compatibilityMode && !letterInfo.orientationFlag) || !letterInfo.compatibilityMode) {
344            if (letterInfo.currentAngle < letterInfo.endAngle) {
345                angle = letterInfo.endAngle - letterInfo.currentAngle;
346            } else if (letterInfo.currentAngle < letterInfo.startAngle) {
347                angle = letterInfo.startAngle - letterInfo.currentAngle;
348            } else {
349                return false;
350            }
351        }
352    }
353
354    return true;
355}
356
357void DrawLabel::OnCalculatedClockwise(const ArcLetterInfo& letterInfo, const uint16_t sizePerPx,
358                                      const uint16_t cols, const int16_t offsetX, uint16_t& begin,
359                                      uint16_t& copyCols, TextInRange& range)
360{
361    if (!letterInfo.directFlag) {
362        return;
363    }
364    if ((letterInfo.compatibilityMode && letterInfo.orientationFlag) || !letterInfo.compatibilityMode) {
365        if (letterInfo.currentAngle > letterInfo.endAngle) {
366            if (offsetX >= cols) {
367                range = TextInRange::OUT_RANGE;
368            }
369            copyCols = cols - offsetX;
370        } else if (letterInfo.currentAngle > letterInfo.startAngle) {
371            if (offsetX >= cols) {
372                range = TextInRange::IN_RANGE;
373            }
374            copyCols = offsetX;
375            begin = (cols - offsetX) * sizePerPx;
376        }
377    } else {
378        if (letterInfo.currentAngle > letterInfo.endAngle) {
379            if (offsetX >= cols) {
380                range = TextInRange::OUT_RANGE;
381            }
382            copyCols = cols - offsetX;
383            begin = offsetX * sizePerPx;
384        } else if (letterInfo.currentAngle > letterInfo.startAngle) {
385            if (offsetX >= cols) {
386                range = TextInRange::IN_RANGE;
387            }
388            copyCols = offsetX;
389        }
390    }
391}
392
393void DrawLabel::OnCalculatedAnticlockwise(const ArcLetterInfo& letterInfo, const uint16_t sizePerPx,
394                                          const uint16_t cols, const int16_t offsetX, uint16_t& begin,
395                                          uint16_t& copyCols, TextInRange& range)
396{
397    if (letterInfo.directFlag) {
398        return;
399    }
400    if (letterInfo.compatibilityMode && letterInfo.orientationFlag) {
401        if (letterInfo.currentAngle < letterInfo.endAngle) {
402            if (offsetX >= cols) {
403                range = TextInRange::OUT_RANGE;
404            }
405            copyCols = cols - offsetX;
406            begin = offsetX * sizePerPx;
407        } else if (letterInfo.currentAngle < letterInfo.startAngle) {
408            if (offsetX >= cols) {
409                range = TextInRange::IN_RANGE;
410            }
411            copyCols = offsetX;
412        }
413    } else if ((letterInfo.compatibilityMode && !letterInfo.orientationFlag) || !letterInfo.compatibilityMode) {
414        if (letterInfo.currentAngle < letterInfo.endAngle) {
415            if (offsetX >= cols) {
416                range = TextInRange::OUT_RANGE;
417            }
418            copyCols = cols - offsetX;
419        } else if (letterInfo.currentAngle < letterInfo.startAngle) {
420            if (offsetX >= cols) {
421                range = TextInRange::IN_RANGE;
422            }
423            copyCols = offsetX;
424            begin = (cols - offsetX) * sizePerPx;
425        }
426    }
427}
428
429void DrawLabel::CalculatedBeginAndCopySize(const ArcLetterInfo& letterInfo, const uint16_t sizePerPx,
430                                           const uint16_t cols, const int16_t offsetX, uint16_t& begin,
431                                           uint16_t& copyCols, TextInRange& range)
432{
433    if (letterInfo.directFlag) {
434        OnCalculatedClockwise(letterInfo, sizePerPx, cols, offsetX, begin, copyCols, range);
435    } else {
436        OnCalculatedAnticlockwise(letterInfo, sizePerPx, cols, offsetX, begin, copyCols, range);
437    }
438}
439
440bool DrawLabel::CalculatedTransformDataInfo(uint8_t** buffer, TransformDataInfo& letterTranDataInfo,
441    const ArcLetterInfo& letterInfo)
442{
443    float angle = 0.0f;
444    if (DrawLabel::CalculatedClipAngle(letterInfo, angle) == false) {
445        return false;
446    }
447    if (angle >= -EPSINON && angle <= EPSINON) {
448        return true;
449    }
450
451    int16_t offsetX = static_cast<uint16_t>(angle * letterInfo.radius * UI_PI / SEMICIRCLE_IN_DEGREE);
452    uint16_t copyCols = 0;
453    uint16_t begin = 0;
454    uint16_t sizePerPx = letterTranDataInfo.pxSize / 8; // 8 bit
455    TextInRange range = TextInRange::NEED_CLIP;
456    uint16_t cols = letterTranDataInfo.header.width;
457    uint16_t rows = letterTranDataInfo.header.height;
458    DrawLabel::CalculatedBeginAndCopySize(letterInfo, sizePerPx, cols, offsetX, begin, copyCols, range);
459    if (range == TextInRange::IN_RANGE) {
460        return true;
461    } else if (range == TextInRange::OUT_RANGE) {
462        return false;
463    }
464
465    const uint8_t* fontMap = letterTranDataInfo.data;
466
467    uint32_t size = cols * rows * sizePerPx;
468    *buffer = static_cast<uint8_t*>(UIMalloc(size));
469    if (*buffer == nullptr) {
470        return false;
471    }
472
473    if (memset_s(*buffer, size, 0, size) != EOK) {
474        UIFree(*buffer);
475        return false;
476    }
477
478    for (uint16_t i = 0; i < rows; i++) {
479        uint16_t beginSize = i * cols * sizePerPx + begin;
480        uint16_t copySize = copyCols * sizePerPx;
481        if (memcpy_s(*buffer + beginSize, copySize, fontMap + beginSize, copySize) != EOK) {
482            UIFree(*buffer);
483            return false;
484        }
485    }
486    letterTranDataInfo.data = *buffer;
487    return true;
488}
489
490void DrawLabel::GetLineBackgroundColor(uint16_t letterIndex, List<LineBackgroundColor>* linebackgroundColor,
491                                       bool& havelinebackground, ColorType& linebgColor)
492{
493    if (linebackgroundColor->Size() > 0) {
494        ListNode<LineBackgroundColor>* lbColor = linebackgroundColor->Begin();
495        for (; lbColor != linebackgroundColor->End(); lbColor = lbColor->next_) {
496            uint32_t start = lbColor->data_.start;
497            uint32_t end = lbColor->data_.end;
498            if (letterIndex >= start && letterIndex <= end) {
499                havelinebackground = true;
500                linebgColor = lbColor->data_.linebackgroundColor ;
501            }
502        }
503    }
504};
505
506void DrawLabel::GetBackgroundColor(uint16_t letterIndex, List<BackgroundColor>* backgroundColor,
507                                   bool& havebackground, ColorType& bgColor)
508{
509    if (backgroundColor->Size() > 0) {
510        ListNode<BackgroundColor>* bColor = backgroundColor->Begin();
511        for (; bColor != backgroundColor->End(); bColor = bColor->next_) {
512            uint16_t start = bColor->data_.start;
513            uint16_t end = bColor->data_.end;
514            if (letterIndex >= start && letterIndex <= end) {
515                havebackground = true;
516                bgColor = bColor->data_.backgroundColor ;
517            }
518        }
519    }
520};
521
522void DrawLabel::GetForegroundColor(uint16_t letterIndex, List<ForegroundColor>* foregroundColor, ColorType& fgColor)
523{
524    if (foregroundColor->Size() > 0) {
525        ListNode<ForegroundColor>* fColor = foregroundColor->Begin();
526        for (; fColor != foregroundColor->End(); fColor = fColor->next_) {
527            uint32_t start = fColor->data_.start;
528            uint32_t end = fColor->data_.end;
529            if (letterIndex >= start && letterIndex <= end) {
530                fgColor = fColor->data_.fontColor;
531            }
532        }
533    }
534};
535
536void DrawLabel::DrawLineBackgroundColor(BufferInfo& gfxDstBuffer, uint16_t letterIndex, const LabelLineInfo& labelLine)
537{
538    uint32_t i = 0;
539    while (i < labelLine.lineLength) {
540        TypedText::GetUTF8Next(labelLine.text, i, i);
541        bool havelinebackground = false;
542        ColorType linebackgroundColor;
543        if (labelLine.spannableString != nullptr &&
544            labelLine.spannableString->GetSpannable(letterIndex)) {
545            havelinebackground =
546                labelLine.spannableString->GetLineBackgroundColor(
547                    letterIndex, linebackgroundColor);
548        }
549        if (havelinebackground) {
550            Style style;
551            style.bgColor_ = linebackgroundColor;
552            Rect linebackground(labelLine.mask.GetLeft(), labelLine.pos.y,
553                                labelLine.mask.GetRight(),
554                                labelLine.pos.y + labelLine.lineHeight);
555            BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, labelLine.mask,
556                                                   linebackground, style,
557                                                   linebackgroundColor.alpha);
558        }
559        letterIndex++;
560    }
561};
562} // namespace OHOS
563