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