Merge "Apply font size scale to markup text" 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/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/controller/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                                      1.0f,
233                                      0u,
234                                      characterCount,
235                                      validFonts);
236
237   // 5) Set the bidirectional info per paragraph.
238   Vector<Character> mirroredUtf32Characters;
239   bool              textMirrored = false;
240
241   // Reserve some space for the vector of paragraph's bidirectional info.
242   Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo;
243
244   // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
245   SetBidirectionalInfo(utf32Characters,
246                        scripts,
247                        lineBreakInfo,
248                        0u,
249                        characterCount,
250                        bidirectionalInfo);
251
252   // Create the paragraph info.
253   logicalModel->CreateParagraphInfo(0u,
254                                     characterCount);
255
256   // 6) Set character directions.
257   Vector<CharacterDirection>& characterDirections = logicalModel->mCharacterDirections;
258   if(0u != bidirectionalInfo.Count())
259   {
260     // Only set the character directions if there is right to left characters.
261     GetCharactersDirection(bidirectionalInfo,
262                            characterCount,
263                            0u,
264                            characterCount,
265                            characterDirections);
266
267     // This paragraph has right to left text. Some characters may need to be mirrored.
268     textMirrored = GetMirroredText(utf32Characters,
269                                    characterDirections,
270                                    bidirectionalInfo,
271                                    0u,
272                                    characterCount,
273                                    mirroredUtf32Characters);
274   }
275   else
276   {
277     // There is no right to left characters. Clear the directions vector.
278     characterDirections.Clear();
279   }
280
281   // 7) Shape the text.
282
283   Vector<GlyphInfo>&      glyphs                = visualModel->mGlyphs;
284   Vector<CharacterIndex>& glyphsToCharactersMap = visualModel->mGlyphsToCharacters;
285   Vector<Length>&         charactersPerGlyph    = visualModel->mCharactersPerGlyph;
286   Vector<GlyphIndex>      newParagraphGlyphs;
287
288   const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
289
290   ShapeText(textToShape,
291             lineBreakInfo,
292             scripts,
293             validFonts,
294             0u,
295             0u,
296             characterCount,
297             glyphs,
298             glyphsToCharactersMap,
299             charactersPerGlyph,
300             newParagraphGlyphs);
301
302   // Create the 'number of glyphs' per character and the glyph to character conversion tables.
303   visualModel->CreateGlyphsPerCharacterTable(0u, 0u, characterCount);
304   visualModel->CreateCharacterToGlyphTable(0u, 0u, characterCount);
305
306   visualModel->SetCharacterSpacing(characterSpacing);
307
308   const Length numberOfGlyphs = glyphs.Count();
309
310   // 8) Get the glyph metrics
311   metrics = Metrics::New(fontClient);
312
313   GlyphInfo* glyphsBuffer = glyphs.Begin();
314   metrics->GetGlyphMetrics(glyphsBuffer, numberOfGlyphs);
315
316   // Update the width and advance of all new paragraph characters.
317   for(Vector<GlyphIndex>::ConstIterator it    = newParagraphGlyphs.Begin(),
318                                         endIt = newParagraphGlyphs.End();
319       it != endIt;
320       ++it)
321   {
322     const GlyphIndex index = *it;
323     GlyphInfo&       glyph = *(glyphsBuffer + index);
324
325     glyph.xBearing = 0.f;
326     glyph.width    = 0.f;
327     glyph.advance  = 0.f;
328   }
329
330   // 9) Layout the text
331   Layout::Engine layoutEngine;
332   layoutEngine.SetMetrics(metrics);
333   layoutEngine.SetLayout(Layout::Engine::MULTI_LINE_BOX);
334   layoutEngine.SetDefaultLineSpacing(lineSpacing);
335
336   // Set the layout parameters.
337   textModel->mHorizontalAlignment   = Text::HorizontalAlignment::BEGIN;
338   textModel->mIgnoreSpacesAfterText = true;
339   Layout::Parameters layoutParameters(textArea,
340                                       textModel);
341
342   Vector<LineRun>& lines = visualModel->mLines;
343
344   Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
345   glyphPositions.Resize(numberOfGlyphs);
346
347   layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph(*(utf32Characters.Begin() + (characterCount - 1u)));
348
349   // The initial glyph and the number of glyphs to layout.
350   layoutParameters.startGlyphIndex        = 0u;
351   layoutParameters.numberOfGlyphs         = numberOfGlyphs;
352   layoutParameters.startLineIndex         = 0u;
353   layoutParameters.estimatedNumberOfLines = logicalModel->mParagraphInfo.Count();
354
355   bool isAutoScroll                   = false;
356   bool isAutoScrollMaxTextureExceeded = false;
357   bool isHiddenInputEnabled           = false;
358   layoutEngine.LayoutText(layoutParameters,
359                           layoutSize,
360                           false,
361                           isAutoScroll,
362                           isAutoScrollMaxTextureExceeded,
363                           isHiddenInputEnabled,
364                           ellipsisPosition);
365
366   if(options.align)
367   {
368     float alignmentOffset = 0.f;
369     layoutEngine.Align(textArea,
370                        0u,
371                        characterCount,
372                        Text::HorizontalAlignment::BEGIN,
373                        lines,
374                        alignmentOffset,
375                        Dali::LayoutDirection::LEFT_TO_RIGHT,
376                        false);
377   }
378 }
379
380 void ConfigureTextLabel(ControllerPtr controller)
381 {
382   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
383   fontClient.SetDpi(93u, 93u);
384
385   // Set the text layout as multi-line.
386   controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
387
388   // Set cursor's width to zero.
389   controller->GetLayoutEngine().SetCursorWidth(0);
390
391   InputMethodContext inputMethodContext = InputMethodContext::New();
392   // Disables the text input.
393   controller->EnableTextInput(NULL, inputMethodContext);
394
395   // Disables the vertical scrolling.
396   controller->SetVerticalScrollEnabled(false);
397
398   // Disables the horizontal scrolling.
399   controller->SetHorizontalScrollEnabled(false);
400
401   // Enable the text elide.
402   controller->SetTextElideEnabled(true);
403
404   // Disable match system language direction
405   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
406 }
407
408 void ConfigureTextField(ControllerPtr controller)
409 {
410   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
411   fontClient.SetDpi(93u, 93u);
412
413   // Creates a decorator.
414   Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
415                                                       *controller);
416
417   // Set the text layout as multi-line.
418   controller->GetLayoutEngine().SetLayout(Layout::Engine::SINGLE_LINE_BOX);
419
420   InputMethodContext inputMethodContext = InputMethodContext::New();
421   // Enables the text input.
422   controller->EnableTextInput(decorator, inputMethodContext);
423
424   // Enables the vertical scrolling after the text input has been enabled.
425   controller->SetVerticalScrollEnabled(false);
426
427   // Disables the horizontal scrolling.
428   controller->SetHorizontalScrollEnabled(true);
429
430   // No maximum number of characters.
431   controller->SetMaximumNumberOfCharacters(50u);
432
433   // Disable the text elide.
434   controller->SetTextElideEnabled(false);
435
436   // Disable match system language direction
437   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
438 }
439
440 void ConfigureTextEditor(ControllerPtr controller)
441 {
442   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
443   fontClient.SetDpi(93u, 93u);
444
445   // Creates a decorator.
446   Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
447                                                       *controller);
448
449   // Set the text layout as multi-line.
450   controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
451
452   InputMethodContext inputMethodContext = InputMethodContext::New();
453   // Enables the text input.
454   controller->EnableTextInput(decorator, inputMethodContext);
455
456   // Enables the vertical scrolling after the text input has been enabled.
457   controller->SetVerticalScrollEnabled(true);
458
459   // Disables the horizontal scrolling.
460   controller->SetHorizontalScrollEnabled(false);
461
462   // No maximum number of characters.
463   controller->SetMaximumNumberOfCharacters(std::numeric_limits<Length>::max());
464
465   // Disable the text elide.
466   controller->SetTextElideEnabled(false);
467
468   // Disable match system language direction
469   controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
470 }
471
472 Vector<FontDescriptionRun> CreateSingleFontDescription(
473   const CharacterRun&   characterRun,
474   const std::string     fontFamilyName,
475   const FontWeight      weight,
476   const FontWidth       width,
477   const FontSlant       slant,
478   const PointSize26Dot6 size,
479   const bool            familyDefined,
480   const bool            weightDefined,
481   const bool            widthDefined,
482   const bool            slantDefined,
483   const bool            sizeDefined)
484 {
485   FontDescriptionRun fontDescriptionRun =
486     {
487       characterRun,
488       nullptr,
489       0u,
490       weight,
491       width,
492       slant,
493       size,
494       familyDefined,
495       weightDefined,
496       widthDefined,
497       slantDefined,
498       sizeDefined};
499
500   fontDescriptionRun.familyLength = fontFamilyName.size();
501   fontDescriptionRun.familyName   = new char[fontDescriptionRun.familyLength];
502   memcpy(fontDescriptionRun.familyName, fontFamilyName.c_str(), fontDescriptionRun.familyLength);
503
504   Vector<FontDescriptionRun> fontDescriptionRuns;
505   fontDescriptionRuns.PushBack(fontDescriptionRun);
506
507   return fontDescriptionRuns;
508 }
509
510 } // namespace Text
511
512 } // namespace Toolkit
513
514 } // namespace Dali