Merge "Support paragraph attribute: relative line height" 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                                       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   layoutEngine.LayoutText(layoutParameters,
356                           layoutSize,
357                           false,
358                           isAutoScroll,
359                           ellipsisPosition);
360
361   if(options.align)
362   {
363     float alignmentOffset = 0.f;
364     layoutEngine.Align(textArea,
365                        0u,
366                        characterCount,
367                        Text::HorizontalAlignment::BEGIN,
368                        lines,
369                        alignmentOffset,
370                        Dali::LayoutDirection::LEFT_TO_RIGHT,
371                        false);
372   }
373 }
374
375 void ConfigureTextLabel(ControllerPtr controller)
376 {
377   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
378   fontClient.SetDpi(93u, 93u);
379
380   // Set the text layout as multi-line.
381   controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
382
383   // Set cursor's width to zero.
384   controller->GetLayoutEngine().SetCursorWidth(0);
385
386   InputMethodContext inputMethodContext = InputMethodContext::New();
387   // Disables the text input.
388   controller->EnableTextInput(NULL, inputMethodContext);
389
390   // Disables the vertical scrolling.
391   controller->SetVerticalScrollEnabled(false);
392
393   // Disables the horizontal scrolling.
394   controller->SetHorizontalScrollEnabled(false);
395
396   // Enable the text elide.
397   controller->SetTextElideEnabled(true);
398
399   // Disable match system language direction
400   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
401 }
402
403 void ConfigureTextField(ControllerPtr controller)
404 {
405   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
406   fontClient.SetDpi(93u, 93u);
407
408   // Creates a decorator.
409   Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
410                                                       *controller);
411
412   // Set the text layout as multi-line.
413   controller->GetLayoutEngine().SetLayout(Layout::Engine::SINGLE_LINE_BOX);
414
415   InputMethodContext inputMethodContext = InputMethodContext::New();
416   // Enables the text input.
417   controller->EnableTextInput(decorator, inputMethodContext);
418
419   // Enables the vertical scrolling after the text input has been enabled.
420   controller->SetVerticalScrollEnabled(false);
421
422   // Disables the horizontal scrolling.
423   controller->SetHorizontalScrollEnabled(true);
424
425   // No maximum number of characters.
426   controller->SetMaximumNumberOfCharacters(50u);
427
428   // Disable the text elide.
429   controller->SetTextElideEnabled(false);
430
431   // Disable match system language direction
432   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
433 }
434
435 void ConfigureTextEditor(ControllerPtr controller)
436 {
437   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
438   fontClient.SetDpi(93u, 93u);
439
440   // Creates a decorator.
441   Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
442                                                       *controller);
443
444   // Set the text layout as multi-line.
445   controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
446
447   InputMethodContext inputMethodContext = InputMethodContext::New();
448   // Enables the text input.
449   controller->EnableTextInput(decorator, inputMethodContext);
450
451   // Enables the vertical scrolling after the text input has been enabled.
452   controller->SetVerticalScrollEnabled(true);
453
454   // Disables the horizontal scrolling.
455   controller->SetHorizontalScrollEnabled(false);
456
457   // No maximum number of characters.
458   controller->SetMaximumNumberOfCharacters(std::numeric_limits<Length>::max());
459
460   // Disable the text elide.
461   controller->SetTextElideEnabled(false);
462
463   // Disable match system language direction
464   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
465 }
466
467 Vector<FontDescriptionRun> CreateSingleFontDescription(
468   const CharacterRun&   characterRun,
469   const std::string     fontFamilyName,
470   const FontWeight      weight,
471   const FontWidth       width,
472   const FontSlant       slant,
473   const PointSize26Dot6 size,
474   const bool            familyDefined,
475   const bool            weightDefined,
476   const bool            widthDefined,
477   const bool            slantDefined,
478   const bool            sizeDefined)
479 {
480   FontDescriptionRun fontDescriptionRun =
481     {
482       characterRun,
483       nullptr,
484       0u,
485       weight,
486       width,
487       slant,
488       size,
489       familyDefined,
490       weightDefined,
491       widthDefined,
492       slantDefined,
493       sizeDefined};
494
495   fontDescriptionRun.familyLength = fontFamilyName.size();
496   fontDescriptionRun.familyName   = new char[fontDescriptionRun.familyLength];
497   memcpy(fontDescriptionRun.familyName, fontFamilyName.c_str(), fontDescriptionRun.familyLength);
498
499   Vector<FontDescriptionRun> fontDescriptionRuns;
500   fontDescriptionRuns.PushBack(fontDescriptionRun);
501
502   return fontDescriptionRuns;
503 }
504
505 } // namespace Text
506
507 } // namespace Toolkit
508
509 } // namespace Dali