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