[dali_2.2.35] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / automated-tests / src / dali-toolkit-internal / dali-toolkit-test-utils / toolkit-text-utils.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // FILE HEADER
19 #include "toolkit-text-utils.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <limits>
24 #include <cstring> ///< for memcpy
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/hyphenator.h>
30 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
32 #include <dali-toolkit/internal/text/markup-processor/markup-processor.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/segmentation.h>
35 #include <dali-toolkit/internal/text/shaper.h>
36 #include <dali-toolkit/internal/text/controller/text-controller-impl.h>
37
38 namespace Dali
39 {
40 namespace Toolkit
41 {
42 namespace Text
43 {
44 /**
45  * @brief Frees previously allocated bidirectional resources.
46  *
47  * @param[in] bidirectionalLineInfo Bidirectional info per line.
48  * @param[in] index Index to the first line with bidirectional info to be freed.
49  */
50 void FreeBidirectionalLineInfoResources(Vector<BidirectionalLineInfoRun> bidirectionalLineInfo,
51                                         uint32_t                         index)
52 {
53   // Free the allocated memory used to store the conversion table in the bidirectional line info run.
54   for(Vector<BidirectionalLineInfoRun>::Iterator it    = bidirectionalLineInfo.Begin() + index,
55                                                  endIt = bidirectionalLineInfo.End();
56       it != endIt;
57       ++it)
58   {
59     BidirectionalLineInfoRun& bidiLineInfo = *it;
60
61     free(bidiLineInfo.visualToLogicalMap);
62   }
63 }
64
65 /**
66  * @brief Clear all the model data except for LogicalModel::mText.
67  *
68  * @param[in] characterIndex Clear data starting from the index.
69  */
70 void ClearModelData(CharacterIndex  characterIndex,
71                     LogicalModelPtr logicalModel,
72                     VisualModelPtr  visualModel)
73 {
74   // n.b. This does not Clear the mText from mLogicalModel
75
76   // Frees previously allocated resources.
77   FreeBidirectionalLineInfoResources(logicalModel->mBidirectionalLineInfo, 0u);
78
79   logicalModel->mScriptRuns.Clear();
80   logicalModel->mFontRuns.Clear();
81   logicalModel->mBidirectionalParagraphInfo.Clear();
82   logicalModel->mCharacterDirections.Clear();
83   logicalModel->mBidirectionalLineInfo.Clear();
84   visualModel->mGlyphs.Clear();
85   visualModel->mGlyphsToCharacters.Clear();
86   visualModel->mCharactersToGlyph.Clear();
87   visualModel->mCharactersPerGlyph.Clear();
88   visualModel->mGlyphsPerCharacter.Clear();
89   visualModel->mGlyphPositions.Clear();
90   visualModel->mLines.Clear();
91
92   visualModel->ClearCaches();
93 }
94
95 void CreateTextModel(const std::string&                text,
96                      const Size&                       textArea,
97                      const Vector<FontDescriptionRun>& fontDescriptions,
98                      const LayoutOptions&              options,
99                      Size&                             layoutSize,
100                      ModelPtr&                         textModel,
101                      MetricsPtr&                       metrics,
102                      bool                              markupProcessorEnabled,
103                      LineWrap::Mode                    wrapMode,
104                      bool                              ellipsisEnabled,
105                      DevelText::EllipsisPosition::Type ellipsisPosition,
106                      float                             lineSpacing,
107                      float                             characterSpacing)
108 {
109   textModel                    = Model::New(); ///< Pointer to the text's model.
110   LogicalModelPtr logicalModel = textModel->mLogicalModel;
111   VisualModelPtr  visualModel  = textModel->mVisualModel;
112
113   MarkupProcessData markupProcessData(logicalModel->mColorRuns,
114                                       logicalModel->mFontDescriptionRuns,
115                                       logicalModel->mEmbeddedItems,
116                                       logicalModel->mAnchors,
117                                       logicalModel->mUnderlinedCharacterRuns,
118                                       logicalModel->mBackgroundColorRuns,
119                                       logicalModel->mStrikethroughCharacterRuns,
120                                       logicalModel->mBoundedParagraphRuns,
121                                       logicalModel->mCharacterSpacingCharacterRuns);
122
123   Length         textSize = 0u;
124   const uint8_t* utf8     = NULL;
125   if(markupProcessorEnabled)
126   {
127     ProcessMarkupString(text, markupProcessData);
128     textSize = markupProcessData.markupProcessedText.size();
129
130     // This is a bit horrible but std::string returns a (signed) char*
131     utf8 = reinterpret_cast<const uint8_t*>(markupProcessData.markupProcessedText.c_str());
132   }
133   else
134   {
135     textSize = text.size();
136
137     // This is a bit horrible but std::string returns a (signed) char*
138     utf8 = reinterpret_cast<const uint8_t*>(text.c_str());
139   }
140
141   //Ellipsis
142   textModel->mElideEnabled = ellipsisEnabled;
143   textModel->mVisualModel->SetTextElideEnabled(ellipsisEnabled);
144   textModel->mEllipsisPosition = ellipsisPosition;
145   textModel->mVisualModel->SetEllipsisPosition(ellipsisPosition);
146
147   // 1) Convert to utf32
148   Vector<Character>& utf32Characters = logicalModel->mText;
149   utf32Characters.Resize(textSize);
150
151   // Transform a text array encoded in utf8 into an array encoded in utf32.
152   // It returns the actual number of characters.
153   Length characterCount = Utf8ToUtf32(utf8, textSize, utf32Characters.Begin());
154   utf32Characters.Resize(characterCount);
155
156   // 2) Set the break and paragraph info.
157   Vector<LineBreakInfo>& lineBreakInfo = logicalModel->mLineBreakInfo;
158   lineBreakInfo.Resize(characterCount);
159
160   SetLineBreakInfo(utf32Characters,
161                    0u,
162                    characterCount,
163                    lineBreakInfo);
164
165   if(0u == characterCount)
166   {
167     // Nothing else to do if the number of characters is zero.
168     return;
169   }
170
171   textModel->mLineWrapMode = wrapMode;
172
173   if(textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
174      textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
175   {
176     CharacterIndex end                 = characterCount;
177     LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
178
179     for(CharacterIndex index = 0; index < end; index++)
180     {
181       CharacterIndex wordEnd = index;
182       while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
183       {
184         wordEnd++;
185       }
186
187       if((wordEnd + 1) == end) // add last char
188       {
189         wordEnd++;
190       }
191
192       Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
193
194       for(CharacterIndex i = 0; i < (wordEnd - index); i++)
195       {
196         if(hyphens[i])
197         {
198           *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
199         }
200       }
201
202       index = wordEnd;
203     }
204   }
205
206   // 3) Set the script info.
207   MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
208
209   Vector<ScriptRun>& scripts = logicalModel->mScriptRuns;
210   multilanguageSupport.SetScripts(utf32Characters,
211                                   0u,
212                                   characterCount,
213                                   scripts);
214
215   // 4) Set the font info
216   Vector<FontDescriptionRun>& fontDescriptionRuns = logicalModel->mFontDescriptionRuns;
217   fontDescriptionRuns                             = fontDescriptions;
218   Vector<FontRun>& validFonts                     = logicalModel->mFontRuns;
219
220   // The default font description.
221   TextAbstraction::FontDescription fontDescription;
222
223   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
224   fontClient.SetDpi(96u, 96u);
225
226   // Validates the fonts. If there is a character with no assigned font it sets a default one.
227   // After this call, fonts are validated.
228   multilanguageSupport.ValidateFonts(utf32Characters,
229                                      scripts,
230                                      fontDescriptionRuns,
231                                      fontDescription,
232                                      TextAbstraction::FontClient::DEFAULT_POINT_SIZE,
233                                      1.0f,
234                                      0u,
235                                      characterCount,
236                                      validFonts);
237
238   // 5) Set the bidirectional info per paragraph.
239   Vector<Character> mirroredUtf32Characters;
240   bool              textMirrored = false;
241
242   // Reserve some space for the vector of paragraph's bidirectional info.
243   Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo;
244
245   // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
246   SetBidirectionalInfo(utf32Characters,
247                        scripts,
248                        lineBreakInfo,
249                        0u,
250                        characterCount,
251                        bidirectionalInfo);
252
253   // Create the paragraph info.
254   logicalModel->CreateParagraphInfo(0u,
255                                     characterCount);
256
257   // 6) Set character directions.
258   Vector<CharacterDirection>& characterDirections = logicalModel->mCharacterDirections;
259   if(0u != bidirectionalInfo.Count())
260   {
261     // Only set the character directions if there is right to left characters.
262     GetCharactersDirection(bidirectionalInfo,
263                            characterCount,
264                            0u,
265                            characterCount,
266                            characterDirections);
267
268     // This paragraph has right to left text. Some characters may need to be mirrored.
269     textMirrored = GetMirroredText(utf32Characters,
270                                    characterDirections,
271                                    bidirectionalInfo,
272                                    0u,
273                                    characterCount,
274                                    mirroredUtf32Characters);
275   }
276   else
277   {
278     // There is no right to left characters. Clear the directions vector.
279     characterDirections.Clear();
280   }
281
282   // 7) Shape the text.
283
284   Vector<GlyphInfo>&      glyphs                = visualModel->mGlyphs;
285   Vector<CharacterIndex>& glyphsToCharactersMap = visualModel->mGlyphsToCharacters;
286   Vector<Length>&         charactersPerGlyph    = visualModel->mCharactersPerGlyph;
287   Vector<GlyphIndex>      newParagraphGlyphs;
288
289   const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
290
291   ShapeText(textToShape,
292             lineBreakInfo,
293             scripts,
294             validFonts,
295             0u,
296             0u,
297             characterCount,
298             glyphs,
299             glyphsToCharactersMap,
300             charactersPerGlyph,
301             newParagraphGlyphs);
302
303   // Create the 'number of glyphs' per character and the glyph to character conversion tables.
304   visualModel->CreateGlyphsPerCharacterTable(0u, 0u, characterCount);
305   visualModel->CreateCharacterToGlyphTable(0u, 0u, characterCount);
306
307   visualModel->SetCharacterSpacing(characterSpacing);
308
309   const Length numberOfGlyphs = glyphs.Count();
310
311   // 8) Get the glyph metrics
312   metrics = Metrics::New(fontClient);
313
314   GlyphInfo* glyphsBuffer = glyphs.Begin();
315   metrics->GetGlyphMetrics(glyphsBuffer, numberOfGlyphs);
316
317   // Update the width and advance of all new paragraph characters.
318   for(Vector<GlyphIndex>::ConstIterator it    = newParagraphGlyphs.Begin(),
319                                         endIt = newParagraphGlyphs.End();
320       it != endIt;
321       ++it)
322   {
323     const GlyphIndex index = *it;
324     GlyphInfo&       glyph = *(glyphsBuffer + index);
325
326     glyph.xBearing = 0.f;
327     glyph.width    = 0.f;
328     glyph.advance  = 0.f;
329   }
330
331   // 9) Layout the text
332   Layout::Engine layoutEngine;
333   layoutEngine.SetMetrics(metrics);
334   layoutEngine.SetLayout(Layout::Engine::MULTI_LINE_BOX);
335   layoutEngine.SetDefaultLineSpacing(lineSpacing);
336
337   // Set the layout parameters.
338   textModel->mHorizontalAlignment   = Text::HorizontalAlignment::BEGIN;
339   textModel->mIgnoreSpacesAfterText = true;
340   Layout::Parameters layoutParameters(textArea,
341                                       textModel);
342
343   Vector<LineRun>& lines = visualModel->mLines;
344
345   Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
346   glyphPositions.Resize(numberOfGlyphs);
347
348   layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph(*(utf32Characters.Begin() + (characterCount - 1u)));
349
350   // The initial glyph and the number of glyphs to layout.
351   layoutParameters.startGlyphIndex        = 0u;
352   layoutParameters.numberOfGlyphs         = numberOfGlyphs;
353   layoutParameters.startLineIndex         = 0u;
354   layoutParameters.estimatedNumberOfLines = logicalModel->mParagraphInfo.Count();
355
356   bool isAutoScroll                   = false;
357   bool isAutoScrollMaxTextureExceeded = false;
358   bool isHiddenInputEnabled           = false;
359   layoutEngine.LayoutText(layoutParameters,
360                           layoutSize,
361                           false,
362                           isAutoScroll,
363                           isAutoScrollMaxTextureExceeded,
364                           isHiddenInputEnabled,
365                           ellipsisPosition);
366
367   if(options.align)
368   {
369     float alignmentOffset = 0.f;
370     layoutEngine.Align(textArea,
371                        0u,
372                        characterCount,
373                        Text::HorizontalAlignment::BEGIN,
374                        lines,
375                        alignmentOffset,
376                        Dali::LayoutDirection::LEFT_TO_RIGHT,
377                        false);
378   }
379 }
380
381 void ConfigureTextLabel(ControllerPtr controller)
382 {
383   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
384   fontClient.SetDpi(93u, 93u);
385
386   // Set the text layout as multi-line.
387   controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
388
389   // Set cursor's width to zero.
390   controller->GetLayoutEngine().SetCursorWidth(0);
391
392   InputMethodContext inputMethodContext = InputMethodContext::New();
393   // Disables the text input.
394   controller->EnableTextInput(NULL, inputMethodContext);
395
396   // Disables the vertical scrolling.
397   controller->SetVerticalScrollEnabled(false);
398
399   // Disables the horizontal scrolling.
400   controller->SetHorizontalScrollEnabled(false);
401
402   // Enable the text elide.
403   controller->SetTextElideEnabled(true);
404
405   // Disable match system language direction
406   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
407 }
408
409 void ConfigureTextField(ControllerPtr controller)
410 {
411   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
412   fontClient.SetDpi(93u, 93u);
413
414   // Creates a decorator.
415   Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
416                                                       *controller);
417
418   // Set the text layout as multi-line.
419   controller->GetLayoutEngine().SetLayout(Layout::Engine::SINGLE_LINE_BOX);
420
421   InputMethodContext inputMethodContext = InputMethodContext::New();
422   // Enables the text input.
423   controller->EnableTextInput(decorator, inputMethodContext);
424
425   // Enables the vertical scrolling after the text input has been enabled.
426   controller->SetVerticalScrollEnabled(false);
427
428   // Disables the horizontal scrolling.
429   controller->SetHorizontalScrollEnabled(true);
430
431   // No maximum number of characters.
432   controller->SetMaximumNumberOfCharacters(50u);
433
434   // Disable the text elide.
435   controller->SetTextElideEnabled(false);
436
437   // Disable match system language direction
438   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
439 }
440
441 void ConfigureTextEditor(ControllerPtr controller)
442 {
443   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
444   fontClient.SetDpi(93u, 93u);
445
446   // Creates a decorator.
447   Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
448                                                       *controller);
449
450   // Set the text layout as multi-line.
451   controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
452
453   InputMethodContext inputMethodContext = InputMethodContext::New();
454   // Enables the text input.
455   controller->EnableTextInput(decorator, inputMethodContext);
456
457   // Enables the vertical scrolling after the text input has been enabled.
458   controller->SetVerticalScrollEnabled(true);
459
460   // Disables the horizontal scrolling.
461   controller->SetHorizontalScrollEnabled(false);
462
463   // No maximum number of characters.
464   controller->SetMaximumNumberOfCharacters(std::numeric_limits<Length>::max());
465
466   // Disable the text elide.
467   controller->SetTextElideEnabled(false);
468
469   // Disable match system language direction
470   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
471 }
472
473 Vector<FontDescriptionRun> CreateSingleFontDescription(
474   const CharacterRun&   characterRun,
475   const std::string     fontFamilyName,
476   const FontWeight      weight,
477   const FontWidth       width,
478   const FontSlant       slant,
479   const PointSize26Dot6 size,
480   const bool            familyDefined,
481   const bool            weightDefined,
482   const bool            widthDefined,
483   const bool            slantDefined,
484   const bool            sizeDefined)
485 {
486   FontDescriptionRun fontDescriptionRun =
487     {
488       characterRun,
489       nullptr,
490       0u,
491       weight,
492       width,
493       slant,
494       size,
495       familyDefined,
496       weightDefined,
497       widthDefined,
498       slantDefined,
499       sizeDefined};
500
501   fontDescriptionRun.familyLength = fontFamilyName.size();
502   fontDescriptionRun.familyName   = new char[fontDescriptionRun.familyLength];
503   memcpy(fontDescriptionRun.familyName, fontFamilyName.c_str(), fontDescriptionRun.familyLength);
504
505   Vector<FontDescriptionRun> fontDescriptionRuns;
506   fontDescriptionRuns.PushBack(fontDescriptionRun);
507
508   return fontDescriptionRuns;
509 }
510
511 } // namespace Text
512
513 } // namespace Toolkit
514
515 } // namespace Dali