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