2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "toolkit-text-utils.h"
22 #include <dali/devel-api/text-abstraction/font-client.h>
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>
44 * @brief Frees previously allocated bidirectional resources.
46 * @param[in] bidirectionalLineInfo Bidirectional info per line.
47 * @param[in] index Index to the first line with bidirectional info to be freed.
49 void FreeBidirectionalLineInfoResources(Vector<BidirectionalLineInfoRun> bidirectionalLineInfo,
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();
58 BidirectionalLineInfoRun& bidiLineInfo = *it;
60 free(bidiLineInfo.visualToLogicalMap);
65 * @brief Clear all the model data except for LogicalModel::mText.
67 * @param[in] characterIndex Clear data starting from the index.
69 void ClearModelData(CharacterIndex characterIndex,
70 LogicalModelPtr logicalModel,
71 VisualModelPtr visualModel)
73 // n.b. This does not Clear the mText from mLogicalModel
75 // Frees previously allocated resources.
76 FreeBidirectionalLineInfoResources(logicalModel->mBidirectionalLineInfo, 0u);
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();
91 visualModel->ClearCaches();
94 void CreateTextModel(const std::string& text,
96 const Vector<FontDescriptionRun>& fontDescriptions,
97 const LayoutOptions& options,
101 bool markupProcessorEnabled,
102 LineWrap::Mode wrapMode,
103 bool ellipsisEnabled,
104 DevelText::EllipsisPosition::Type ellipsisPosition,
106 float characterSpacing)
108 textModel = Model::New(); ///< Pointer to the text's model.
109 LogicalModelPtr logicalModel = textModel->mLogicalModel;
110 VisualModelPtr visualModel = textModel->mVisualModel;
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);
122 Length textSize = 0u;
123 const uint8_t* utf8 = NULL;
124 if(markupProcessorEnabled)
126 ProcessMarkupString(text, markupProcessData);
127 textSize = markupProcessData.markupProcessedText.size();
129 // This is a bit horrible but std::string returns a (signed) char*
130 utf8 = reinterpret_cast<const uint8_t*>(markupProcessData.markupProcessedText.c_str());
134 textSize = text.size();
136 // This is a bit horrible but std::string returns a (signed) char*
137 utf8 = reinterpret_cast<const uint8_t*>(text.c_str());
141 textModel->mElideEnabled = ellipsisEnabled;
142 textModel->mVisualModel->SetTextElideEnabled(ellipsisEnabled);
143 textModel->mEllipsisPosition = ellipsisPosition;
144 textModel->mVisualModel->SetEllipsisPosition(ellipsisPosition);
146 // 1) Convert to utf32
147 Vector<Character>& utf32Characters = logicalModel->mText;
148 utf32Characters.Resize(textSize);
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);
155 // 2) Set the break and paragraph info.
156 Vector<LineBreakInfo>& lineBreakInfo = logicalModel->mLineBreakInfo;
157 lineBreakInfo.Resize(characterCount);
159 SetLineBreakInfo(utf32Characters,
164 if(0u == characterCount)
166 // Nothing else to do if the number of characters is zero.
170 textModel->mLineWrapMode = wrapMode;
172 if(textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
173 textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
175 CharacterIndex end = characterCount;
176 LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
178 for(CharacterIndex index = 0; index < end; index++)
180 CharacterIndex wordEnd = index;
181 while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
186 if((wordEnd + 1) == end) // add last char
191 Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
193 for(CharacterIndex i = 0; i < (wordEnd - index); i++)
197 *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
205 // 3) Set the script info.
206 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
208 Vector<ScriptRun>& scripts = logicalModel->mScriptRuns;
209 multilanguageSupport.SetScripts(utf32Characters,
214 // 4) Set the font info
215 Vector<FontDescriptionRun>& fontDescriptionRuns = logicalModel->mFontDescriptionRuns;
216 fontDescriptionRuns = fontDescriptions;
217 Vector<FontRun>& validFonts = logicalModel->mFontRuns;
219 // The default font description.
220 TextAbstraction::FontDescription fontDescription;
222 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
223 fontClient.SetDpi(96u, 96u);
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,
231 TextAbstraction::FontClient::DEFAULT_POINT_SIZE,
237 // 5) Set the bidirectional info per paragraph.
238 Vector<Character> mirroredUtf32Characters;
239 bool textMirrored = false;
241 // Reserve some space for the vector of paragraph's bidirectional info.
242 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo;
244 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
245 SetBidirectionalInfo(utf32Characters,
252 // Create the paragraph info.
253 logicalModel->CreateParagraphInfo(0u,
256 // 6) Set character directions.
257 Vector<CharacterDirection>& characterDirections = logicalModel->mCharacterDirections;
258 if(0u != bidirectionalInfo.Count())
260 // Only set the character directions if there is right to left characters.
261 GetCharactersDirection(bidirectionalInfo,
265 characterDirections);
267 // This paragraph has right to left text. Some characters may need to be mirrored.
268 textMirrored = GetMirroredText(utf32Characters,
273 mirroredUtf32Characters);
277 // There is no right to left characters. Clear the directions vector.
278 characterDirections.Clear();
281 // 7) Shape the text.
283 Vector<GlyphInfo>& glyphs = visualModel->mGlyphs;
284 Vector<CharacterIndex>& glyphsToCharactersMap = visualModel->mGlyphsToCharacters;
285 Vector<Length>& charactersPerGlyph = visualModel->mCharactersPerGlyph;
286 Vector<GlyphIndex> newParagraphGlyphs;
288 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
290 ShapeText(textToShape,
298 glyphsToCharactersMap,
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);
306 visualModel->SetCharacterSpacing(characterSpacing);
308 const Length numberOfGlyphs = glyphs.Count();
310 // 8) Get the glyph metrics
311 metrics = Metrics::New(fontClient);
313 GlyphInfo* glyphsBuffer = glyphs.Begin();
314 metrics->GetGlyphMetrics(glyphsBuffer, numberOfGlyphs);
316 // Update the width and advance of all new paragraph characters.
317 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(),
318 endIt = newParagraphGlyphs.End();
322 const GlyphIndex index = *it;
323 GlyphInfo& glyph = *(glyphsBuffer + index);
325 glyph.xBearing = 0.f;
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);
336 // Set the layout parameters.
337 textModel->mHorizontalAlignment = Text::HorizontalAlignment::BEGIN;
338 textModel->mIgnoreSpacesAfterText = true;
339 Layout::Parameters layoutParameters(textArea,
342 Vector<LineRun>& lines = visualModel->mLines;
344 Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
345 glyphPositions.Resize(numberOfGlyphs);
347 layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph(*(utf32Characters.Begin() + (characterCount - 1u)));
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();
355 bool isAutoScroll = false;
356 bool isAutoScrollMaxTextureExceeded = false;
357 bool isHiddenInputEnabled = false;
358 layoutEngine.LayoutText(layoutParameters,
362 isAutoScrollMaxTextureExceeded,
363 isHiddenInputEnabled,
368 float alignmentOffset = 0.f;
369 layoutEngine.Align(textArea,
372 Text::HorizontalAlignment::BEGIN,
375 Dali::LayoutDirection::LEFT_TO_RIGHT,
380 void ConfigureTextLabel(ControllerPtr controller)
382 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
383 fontClient.SetDpi(93u, 93u);
385 // Set the text layout as multi-line.
386 controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
388 // Set cursor's width to zero.
389 controller->GetLayoutEngine().SetCursorWidth(0);
391 InputMethodContext inputMethodContext = InputMethodContext::New();
392 // Disables the text input.
393 controller->EnableTextInput(NULL, inputMethodContext);
395 // Disables the vertical scrolling.
396 controller->SetVerticalScrollEnabled(false);
398 // Disables the horizontal scrolling.
399 controller->SetHorizontalScrollEnabled(false);
401 // Enable the text elide.
402 controller->SetTextElideEnabled(true);
404 // Disable match system language direction
405 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
408 void ConfigureTextField(ControllerPtr controller)
410 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
411 fontClient.SetDpi(93u, 93u);
413 // Creates a decorator.
414 Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
417 // Set the text layout as multi-line.
418 controller->GetLayoutEngine().SetLayout(Layout::Engine::SINGLE_LINE_BOX);
420 InputMethodContext inputMethodContext = InputMethodContext::New();
421 // Enables the text input.
422 controller->EnableTextInput(decorator, inputMethodContext);
424 // Enables the vertical scrolling after the text input has been enabled.
425 controller->SetVerticalScrollEnabled(false);
427 // Disables the horizontal scrolling.
428 controller->SetHorizontalScrollEnabled(true);
430 // No maximum number of characters.
431 controller->SetMaximumNumberOfCharacters(50u);
433 // Disable the text elide.
434 controller->SetTextElideEnabled(false);
436 // Disable match system language direction
437 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
440 void ConfigureTextEditor(ControllerPtr controller)
442 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
443 fontClient.SetDpi(93u, 93u);
445 // Creates a decorator.
446 Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
449 // Set the text layout as multi-line.
450 controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
452 InputMethodContext inputMethodContext = InputMethodContext::New();
453 // Enables the text input.
454 controller->EnableTextInput(decorator, inputMethodContext);
456 // Enables the vertical scrolling after the text input has been enabled.
457 controller->SetVerticalScrollEnabled(true);
459 // Disables the horizontal scrolling.
460 controller->SetHorizontalScrollEnabled(false);
462 // No maximum number of characters.
463 controller->SetMaximumNumberOfCharacters(std::numeric_limits<Length>::max());
465 // Disable the text elide.
466 controller->SetTextElideEnabled(false);
468 // Disable match system language direction
469 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
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)
485 FontDescriptionRun fontDescriptionRun =
500 fontDescriptionRun.familyLength = fontFamilyName.size();
501 fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
502 memcpy(fontDescriptionRun.familyName, fontFamilyName.c_str(), fontDescriptionRun.familyLength);
504 Vector<FontDescriptionRun> fontDescriptionRuns;
505 fontDescriptionRuns.PushBack(fontDescriptionRun);
507 return fontDescriptionRuns;
512 } // namespace Toolkit