[dali_2.3.20] 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     MarkupPropertyData markupPropertyData(Color::MEDIUM_BLUE, Color::DARK_MAGENTA);
128
129     ProcessMarkupString(text, markupPropertyData, markupProcessData);
130     textSize = markupProcessData.markupProcessedText.size();
131
132     // This is a bit horrible but std::string returns a (signed) char*
133     utf8 = reinterpret_cast<const uint8_t*>(markupProcessData.markupProcessedText.c_str());
134   }
135   else
136   {
137     textSize = text.size();
138
139     // This is a bit horrible but std::string returns a (signed) char*
140     utf8 = reinterpret_cast<const uint8_t*>(text.c_str());
141   }
142
143   //Ellipsis
144   textModel->mElideEnabled = ellipsisEnabled;
145   textModel->mVisualModel->SetTextElideEnabled(ellipsisEnabled);
146   textModel->mEllipsisPosition = ellipsisPosition;
147   textModel->mVisualModel->SetEllipsisPosition(ellipsisPosition);
148
149   // 1) Convert to utf32
150   Vector<Character>& utf32Characters = logicalModel->mText;
151   utf32Characters.Resize(textSize);
152
153   // Transform a text array encoded in utf8 into an array encoded in utf32.
154   // It returns the actual number of characters.
155   Length characterCount = Utf8ToUtf32(utf8, textSize, utf32Characters.Begin());
156   utf32Characters.Resize(characterCount);
157
158   // 2) Set the break and paragraph info.
159   Vector<LineBreakInfo>& lineBreakInfo = logicalModel->mLineBreakInfo;
160   lineBreakInfo.Resize(characterCount);
161
162   SetLineBreakInfo(utf32Characters,
163                    0u,
164                    characterCount,
165                    lineBreakInfo);
166
167   if(0u == characterCount)
168   {
169     // Nothing else to do if the number of characters is zero.
170     return;
171   }
172
173   textModel->mLineWrapMode = wrapMode;
174
175   if(textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
176      textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
177   {
178     CharacterIndex end                 = characterCount;
179     LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
180
181     for(CharacterIndex index = 0; index < end; index++)
182     {
183       CharacterIndex wordEnd = index;
184       while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
185       {
186         wordEnd++;
187       }
188
189       if((wordEnd + 1) == end) // add last char
190       {
191         wordEnd++;
192       }
193
194       Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
195
196       for(CharacterIndex i = 0; i < (wordEnd - index); i++)
197       {
198         if(hyphens[i])
199         {
200           *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
201         }
202       }
203
204       index = wordEnd;
205     }
206   }
207
208   // 3) Set the script info.
209   MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
210
211   Vector<ScriptRun>& scripts = logicalModel->mScriptRuns;
212   multilanguageSupport.SetScripts(utf32Characters,
213                                   0u,
214                                   characterCount,
215                                   scripts);
216
217   // 4) Set the font info
218   Vector<FontDescriptionRun>& fontDescriptionRuns = logicalModel->mFontDescriptionRuns;
219   fontDescriptionRuns                             = fontDescriptions;
220   Vector<FontRun>& validFonts                     = logicalModel->mFontRuns;
221
222   // The default font description.
223   TextAbstraction::FontDescription fontDescription;
224
225   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
226   fontClient.SetDpi(96u, 96u);
227
228   // Validates the fonts. If there is a character with no assigned font it sets a default one.
229   // After this call, fonts are validated.
230   multilanguageSupport.ValidateFonts(utf32Characters,
231                                      scripts,
232                                      fontDescriptionRuns,
233                                      fontDescription,
234                                      TextAbstraction::FontClient::DEFAULT_POINT_SIZE,
235                                      1.0f,
236                                      0u,
237                                      characterCount,
238                                      validFonts);
239
240   // 5) Set the bidirectional info per paragraph.
241   Vector<Character> mirroredUtf32Characters;
242   bool              textMirrored = false;
243
244   // Reserve some space for the vector of paragraph's bidirectional info.
245   Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo;
246
247   // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
248   SetBidirectionalInfo(utf32Characters,
249                        scripts,
250                        lineBreakInfo,
251                        0u,
252                        characterCount,
253                        bidirectionalInfo);
254
255   // Create the paragraph info.
256   logicalModel->CreateParagraphInfo(0u,
257                                     characterCount);
258
259   // 6) Set character directions.
260   Vector<CharacterDirection>& characterDirections = logicalModel->mCharacterDirections;
261   if(0u != bidirectionalInfo.Count())
262   {
263     // Only set the character directions if there is right to left characters.
264     GetCharactersDirection(bidirectionalInfo,
265                            characterCount,
266                            0u,
267                            characterCount,
268                            characterDirections);
269
270     // This paragraph has right to left text. Some characters may need to be mirrored.
271     textMirrored = GetMirroredText(utf32Characters,
272                                    characterDirections,
273                                    bidirectionalInfo,
274                                    0u,
275                                    characterCount,
276                                    mirroredUtf32Characters);
277   }
278   else
279   {
280     // There is no right to left characters. Clear the directions vector.
281     characterDirections.Clear();
282   }
283
284   // 7) Shape the text.
285
286   Vector<GlyphInfo>&      glyphs                = visualModel->mGlyphs;
287   Vector<CharacterIndex>& glyphsToCharactersMap = visualModel->mGlyphsToCharacters;
288   Vector<Length>&         charactersPerGlyph    = visualModel->mCharactersPerGlyph;
289   Vector<GlyphIndex>      newParagraphGlyphs;
290
291   const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
292
293   ShapeText(textToShape,
294             lineBreakInfo,
295             scripts,
296             validFonts,
297             0u,
298             0u,
299             characterCount,
300             glyphs,
301             glyphsToCharactersMap,
302             charactersPerGlyph,
303             newParagraphGlyphs);
304
305   // Create the 'number of glyphs' per character and the glyph to character conversion tables.
306   visualModel->CreateGlyphsPerCharacterTable(0u, 0u, characterCount);
307   visualModel->CreateCharacterToGlyphTable(0u, 0u, characterCount);
308
309   visualModel->SetCharacterSpacing(characterSpacing);
310
311   const Length numberOfGlyphs = glyphs.Count();
312
313   // 8) Get the glyph metrics
314   metrics = Metrics::New(fontClient);
315
316   GlyphInfo* glyphsBuffer = glyphs.Begin();
317   metrics->GetGlyphMetrics(glyphsBuffer, numberOfGlyphs);
318
319   // Update the width and advance of all new paragraph characters.
320   for(Vector<GlyphIndex>::ConstIterator it    = newParagraphGlyphs.Begin(),
321                                         endIt = newParagraphGlyphs.End();
322       it != endIt;
323       ++it)
324   {
325     const GlyphIndex index = *it;
326     GlyphInfo&       glyph = *(glyphsBuffer + index);
327
328     glyph.xBearing = 0.f;
329     glyph.width    = 0.f;
330     glyph.advance  = 0.f;
331   }
332
333   // 9) Layout the text
334   Layout::Engine layoutEngine;
335   layoutEngine.SetMetrics(metrics);
336   layoutEngine.SetLayout(Layout::Engine::MULTI_LINE_BOX);
337   layoutEngine.SetDefaultLineSpacing(lineSpacing);
338
339   // Set the layout parameters.
340   textModel->mHorizontalAlignment   = Text::HorizontalAlignment::BEGIN;
341   textModel->mIgnoreSpacesAfterText = true;
342   Layout::Parameters layoutParameters(textArea,
343                                       textModel);
344
345   Vector<LineRun>& lines = visualModel->mLines;
346
347   Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
348   glyphPositions.Resize(numberOfGlyphs);
349
350   layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph(*(utf32Characters.Begin() + (characterCount - 1u)));
351
352   // The initial glyph and the number of glyphs to layout.
353   layoutParameters.startGlyphIndex        = 0u;
354   layoutParameters.numberOfGlyphs         = numberOfGlyphs;
355   layoutParameters.startLineIndex         = 0u;
356   layoutParameters.estimatedNumberOfLines = logicalModel->mParagraphInfo.Count();
357
358   bool isAutoScroll                   = false;
359   bool isAutoScrollMaxTextureExceeded = false;
360   bool isHiddenInputEnabled           = false;
361   layoutEngine.LayoutText(layoutParameters,
362                           layoutSize,
363                           false,
364                           isAutoScroll,
365                           isAutoScrollMaxTextureExceeded,
366                           isHiddenInputEnabled,
367                           ellipsisPosition);
368
369   if(options.align)
370   {
371     float alignmentOffset = 0.f;
372     layoutEngine.Align(textArea,
373                        0u,
374                        characterCount,
375                        Text::HorizontalAlignment::BEGIN,
376                        lines,
377                        alignmentOffset,
378                        Dali::LayoutDirection::LEFT_TO_RIGHT,
379                        false);
380   }
381 }
382
383 void ConfigureTextLabel(ControllerPtr controller)
384 {
385   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
386   fontClient.SetDpi(93u, 93u);
387
388   // Set the text layout as multi-line.
389   controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
390
391   // Set cursor's width to zero.
392   controller->GetLayoutEngine().SetCursorWidth(0);
393
394   InputMethodContext inputMethodContext = InputMethodContext::New();
395   // Disables the text input.
396   controller->EnableTextInput(NULL, inputMethodContext);
397
398   // Disables the vertical scrolling.
399   controller->SetVerticalScrollEnabled(false);
400
401   // Disables the horizontal scrolling.
402   controller->SetHorizontalScrollEnabled(false);
403
404   // Enable the text elide.
405   controller->SetTextElideEnabled(true);
406
407   // Disable match system language direction
408   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
409 }
410
411 void ConfigureTextField(ControllerPtr controller)
412 {
413   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
414   fontClient.SetDpi(93u, 93u);
415
416   // Creates a decorator.
417   Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
418                                                       *controller);
419
420   // Set the text layout as multi-line.
421   controller->GetLayoutEngine().SetLayout(Layout::Engine::SINGLE_LINE_BOX);
422
423   InputMethodContext inputMethodContext = InputMethodContext::New();
424   // Enables the text input.
425   controller->EnableTextInput(decorator, inputMethodContext);
426
427   // Enables the vertical scrolling after the text input has been enabled.
428   controller->SetVerticalScrollEnabled(false);
429
430   // Disables the horizontal scrolling.
431   controller->SetHorizontalScrollEnabled(true);
432
433   // No maximum number of characters.
434   controller->SetMaximumNumberOfCharacters(50u);
435
436   // Disable the text elide.
437   controller->SetTextElideEnabled(false);
438
439   // Disable match system language direction
440   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
441 }
442
443 void ConfigureTextEditor(ControllerPtr controller)
444 {
445   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
446   fontClient.SetDpi(93u, 93u);
447
448   // Creates a decorator.
449   Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
450                                                       *controller);
451
452   // Set the text layout as multi-line.
453   controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
454
455   InputMethodContext inputMethodContext = InputMethodContext::New();
456   // Enables the text input.
457   controller->EnableTextInput(decorator, inputMethodContext);
458
459   // Enables the vertical scrolling after the text input has been enabled.
460   controller->SetVerticalScrollEnabled(true);
461
462   // Disables the horizontal scrolling.
463   controller->SetHorizontalScrollEnabled(false);
464
465   // No maximum number of characters.
466   controller->SetMaximumNumberOfCharacters(std::numeric_limits<Length>::max());
467
468   // Disable the text elide.
469   controller->SetTextElideEnabled(false);
470
471   // Disable match system language direction
472   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
473 }
474
475 Vector<FontDescriptionRun> CreateSingleFontDescription(
476   const CharacterRun&   characterRun,
477   const std::string     fontFamilyName,
478   const FontWeight      weight,
479   const FontWidth       width,
480   const FontSlant       slant,
481   const PointSize26Dot6 size,
482   const bool            familyDefined,
483   const bool            weightDefined,
484   const bool            widthDefined,
485   const bool            slantDefined,
486   const bool            sizeDefined)
487 {
488   FontDescriptionRun fontDescriptionRun =
489     {
490       characterRun,
491       nullptr,
492       0u,
493       weight,
494       width,
495       slant,
496       size,
497       familyDefined,
498       weightDefined,
499       widthDefined,
500       slantDefined,
501       sizeDefined};
502
503   fontDescriptionRun.familyLength = fontFamilyName.size();
504   fontDescriptionRun.familyName   = new char[fontDescriptionRun.familyLength];
505   memcpy(fontDescriptionRun.familyName, fontFamilyName.c_str(), fontDescriptionRun.familyLength);
506
507   Vector<FontDescriptionRun> fontDescriptionRuns;
508   fontDescriptionRuns.PushBack(fontDescriptionRun);
509
510   return fontDescriptionRuns;
511 }
512
513 } // namespace Text
514
515 } // namespace Toolkit
516
517 } // namespace Dali