2 * Copyright (c) 2023 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>
24 #include <cstring> ///< for memcpy
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/hyphenator.h>
30 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
32 #include <dali-toolkit/internal/text/markup-processor/markup-processor.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/segmentation.h>
35 #include <dali-toolkit/internal/text/shaper.h>
36 #include <dali-toolkit/internal/text/controller/text-controller-impl.h>
45 * @brief Frees previously allocated bidirectional resources.
47 * @param[in] bidirectionalLineInfo Bidirectional info per line.
48 * @param[in] index Index to the first line with bidirectional info to be freed.
50 void FreeBidirectionalLineInfoResources(Vector<BidirectionalLineInfoRun> bidirectionalLineInfo,
53 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
54 for(Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfo.Begin() + index,
55 endIt = bidirectionalLineInfo.End();
59 BidirectionalLineInfoRun& bidiLineInfo = *it;
61 free(bidiLineInfo.visualToLogicalMap);
66 * @brief Clear all the model data except for LogicalModel::mText.
68 * @param[in] characterIndex Clear data starting from the index.
70 void ClearModelData(CharacterIndex characterIndex,
71 LogicalModelPtr logicalModel,
72 VisualModelPtr visualModel)
74 // n.b. This does not Clear the mText from mLogicalModel
76 // Frees previously allocated resources.
77 FreeBidirectionalLineInfoResources(logicalModel->mBidirectionalLineInfo, 0u);
79 logicalModel->mScriptRuns.Clear();
80 logicalModel->mFontRuns.Clear();
81 logicalModel->mBidirectionalParagraphInfo.Clear();
82 logicalModel->mCharacterDirections.Clear();
83 logicalModel->mBidirectionalLineInfo.Clear();
84 visualModel->mGlyphs.Clear();
85 visualModel->mGlyphsToCharacters.Clear();
86 visualModel->mCharactersToGlyph.Clear();
87 visualModel->mCharactersPerGlyph.Clear();
88 visualModel->mGlyphsPerCharacter.Clear();
89 visualModel->mGlyphPositions.Clear();
90 visualModel->mLines.Clear();
92 visualModel->ClearCaches();
95 void CreateTextModel(const std::string& text,
97 const Vector<FontDescriptionRun>& fontDescriptions,
98 const LayoutOptions& options,
102 bool markupProcessorEnabled,
103 LineWrap::Mode wrapMode,
104 bool ellipsisEnabled,
105 DevelText::EllipsisPosition::Type ellipsisPosition,
107 float characterSpacing)
109 textModel = Model::New(); ///< Pointer to the text's model.
110 LogicalModelPtr logicalModel = textModel->mLogicalModel;
111 VisualModelPtr visualModel = textModel->mVisualModel;
113 MarkupProcessData markupProcessData(logicalModel->mColorRuns,
114 logicalModel->mFontDescriptionRuns,
115 logicalModel->mEmbeddedItems,
116 logicalModel->mAnchors,
117 logicalModel->mUnderlinedCharacterRuns,
118 logicalModel->mBackgroundColorRuns,
119 logicalModel->mStrikethroughCharacterRuns,
120 logicalModel->mBoundedParagraphRuns,
121 logicalModel->mCharacterSpacingCharacterRuns);
123 Length textSize = 0u;
124 const uint8_t* utf8 = NULL;
125 if(markupProcessorEnabled)
127 MarkupPropertyData markupPropertyData(Color::MEDIUM_BLUE, Color::DARK_MAGENTA);
129 ProcessMarkupString(text, markupPropertyData, markupProcessData);
130 textSize = markupProcessData.markupProcessedText.size();
132 // This is a bit horrible but std::string returns a (signed) char*
133 utf8 = reinterpret_cast<const uint8_t*>(markupProcessData.markupProcessedText.c_str());
137 textSize = text.size();
139 // This is a bit horrible but std::string returns a (signed) char*
140 utf8 = reinterpret_cast<const uint8_t*>(text.c_str());
144 textModel->mElideEnabled = ellipsisEnabled;
145 textModel->mVisualModel->SetTextElideEnabled(ellipsisEnabled);
146 textModel->mEllipsisPosition = ellipsisPosition;
147 textModel->mVisualModel->SetEllipsisPosition(ellipsisPosition);
149 // 1) Convert to utf32
150 Vector<Character>& utf32Characters = logicalModel->mText;
151 utf32Characters.Resize(textSize);
153 // Transform a text array encoded in utf8 into an array encoded in utf32.
154 // It returns the actual number of characters.
155 Length characterCount = Utf8ToUtf32(utf8, textSize, utf32Characters.Begin());
156 utf32Characters.Resize(characterCount);
158 // 2) Set the break and paragraph info.
159 Vector<LineBreakInfo>& lineBreakInfo = logicalModel->mLineBreakInfo;
160 lineBreakInfo.Resize(characterCount);
162 SetLineBreakInfo(utf32Characters,
167 if(0u == characterCount)
169 // Nothing else to do if the number of characters is zero.
173 textModel->mLineWrapMode = wrapMode;
175 if(textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
176 textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
178 CharacterIndex end = characterCount;
179 LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
181 for(CharacterIndex index = 0; index < end; index++)
183 CharacterIndex wordEnd = index;
184 while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
189 if((wordEnd + 1) == end) // add last char
194 Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
196 for(CharacterIndex i = 0; i < (wordEnd - index); i++)
200 *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
208 // 3) Set the script info.
209 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
211 Vector<ScriptRun>& scripts = logicalModel->mScriptRuns;
212 multilanguageSupport.SetScripts(utf32Characters,
217 // 4) Set the font info
218 Vector<FontDescriptionRun>& fontDescriptionRuns = logicalModel->mFontDescriptionRuns;
219 fontDescriptionRuns = fontDescriptions;
220 Vector<FontRun>& validFonts = logicalModel->mFontRuns;
222 // The default font description.
223 TextAbstraction::FontDescription fontDescription;
225 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
226 fontClient.SetDpi(96u, 96u);
228 // Validates the fonts. If there is a character with no assigned font it sets a default one.
229 // After this call, fonts are validated.
230 multilanguageSupport.ValidateFonts(utf32Characters,
234 TextAbstraction::FontClient::DEFAULT_POINT_SIZE,
240 // 5) Set the bidirectional info per paragraph.
241 Vector<Character> mirroredUtf32Characters;
242 bool textMirrored = false;
244 // Reserve some space for the vector of paragraph's bidirectional info.
245 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo;
247 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
248 SetBidirectionalInfo(utf32Characters,
255 // Create the paragraph info.
256 logicalModel->CreateParagraphInfo(0u,
259 // 6) Set character directions.
260 Vector<CharacterDirection>& characterDirections = logicalModel->mCharacterDirections;
261 if(0u != bidirectionalInfo.Count())
263 // Only set the character directions if there is right to left characters.
264 GetCharactersDirection(bidirectionalInfo,
268 characterDirections);
270 // This paragraph has right to left text. Some characters may need to be mirrored.
271 textMirrored = GetMirroredText(utf32Characters,
276 mirroredUtf32Characters);
280 // There is no right to left characters. Clear the directions vector.
281 characterDirections.Clear();
284 // 7) Shape the text.
286 Vector<GlyphInfo>& glyphs = visualModel->mGlyphs;
287 Vector<CharacterIndex>& glyphsToCharactersMap = visualModel->mGlyphsToCharacters;
288 Vector<Length>& charactersPerGlyph = visualModel->mCharactersPerGlyph;
289 Vector<GlyphIndex> newParagraphGlyphs;
291 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
293 ShapeText(textToShape,
301 glyphsToCharactersMap,
305 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
306 visualModel->CreateGlyphsPerCharacterTable(0u, 0u, characterCount);
307 visualModel->CreateCharacterToGlyphTable(0u, 0u, characterCount);
309 visualModel->SetCharacterSpacing(characterSpacing);
311 const Length numberOfGlyphs = glyphs.Count();
313 // 8) Get the glyph metrics
314 metrics = Metrics::New(fontClient);
316 GlyphInfo* glyphsBuffer = glyphs.Begin();
317 metrics->GetGlyphMetrics(glyphsBuffer, numberOfGlyphs);
319 // Update the width and advance of all new paragraph characters.
320 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(),
321 endIt = newParagraphGlyphs.End();
325 const GlyphIndex index = *it;
326 GlyphInfo& glyph = *(glyphsBuffer + index);
328 glyph.xBearing = 0.f;
333 // 9) Layout the text
334 Layout::Engine layoutEngine;
335 layoutEngine.SetMetrics(metrics);
336 layoutEngine.SetLayout(Layout::Engine::MULTI_LINE_BOX);
337 layoutEngine.SetDefaultLineSpacing(lineSpacing);
339 // Set the layout parameters.
340 textModel->mHorizontalAlignment = Text::HorizontalAlignment::BEGIN;
341 textModel->mIgnoreSpacesAfterText = true;
342 Layout::Parameters layoutParameters(textArea,
345 Vector<LineRun>& lines = visualModel->mLines;
347 Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
348 glyphPositions.Resize(numberOfGlyphs);
350 layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph(*(utf32Characters.Begin() + (characterCount - 1u)));
352 // The initial glyph and the number of glyphs to layout.
353 layoutParameters.startGlyphIndex = 0u;
354 layoutParameters.numberOfGlyphs = numberOfGlyphs;
355 layoutParameters.startLineIndex = 0u;
356 layoutParameters.estimatedNumberOfLines = logicalModel->mParagraphInfo.Count();
358 bool isAutoScroll = false;
359 bool isAutoScrollMaxTextureExceeded = false;
360 bool isHiddenInputEnabled = false;
361 layoutEngine.LayoutText(layoutParameters,
365 isAutoScrollMaxTextureExceeded,
366 isHiddenInputEnabled,
371 float alignmentOffset = 0.f;
372 layoutEngine.Align(textArea,
375 Text::HorizontalAlignment::BEGIN,
378 Dali::LayoutDirection::LEFT_TO_RIGHT,
383 void ConfigureTextLabel(ControllerPtr controller)
385 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
386 fontClient.SetDpi(93u, 93u);
388 // Set the text layout as multi-line.
389 controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
391 // Set cursor's width to zero.
392 controller->GetLayoutEngine().SetCursorWidth(0);
394 InputMethodContext inputMethodContext = InputMethodContext::New();
395 // Disables the text input.
396 controller->EnableTextInput(NULL, inputMethodContext);
398 // Disables the vertical scrolling.
399 controller->SetVerticalScrollEnabled(false);
401 // Disables the horizontal scrolling.
402 controller->SetHorizontalScrollEnabled(false);
404 // Enable the text elide.
405 controller->SetTextElideEnabled(true);
407 // Disable match system language direction
408 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
411 void ConfigureTextField(ControllerPtr controller)
413 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
414 fontClient.SetDpi(93u, 93u);
416 // Creates a decorator.
417 Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
420 // Set the text layout as multi-line.
421 controller->GetLayoutEngine().SetLayout(Layout::Engine::SINGLE_LINE_BOX);
423 InputMethodContext inputMethodContext = InputMethodContext::New();
424 // Enables the text input.
425 controller->EnableTextInput(decorator, inputMethodContext);
427 // Enables the vertical scrolling after the text input has been enabled.
428 controller->SetVerticalScrollEnabled(false);
430 // Disables the horizontal scrolling.
431 controller->SetHorizontalScrollEnabled(true);
433 // No maximum number of characters.
434 controller->SetMaximumNumberOfCharacters(50u);
436 // Disable the text elide.
437 controller->SetTextElideEnabled(false);
439 // Disable match system language direction
440 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
443 void ConfigureTextEditor(ControllerPtr controller)
445 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
446 fontClient.SetDpi(93u, 93u);
448 // Creates a decorator.
449 Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
452 // Set the text layout as multi-line.
453 controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
455 InputMethodContext inputMethodContext = InputMethodContext::New();
456 // Enables the text input.
457 controller->EnableTextInput(decorator, inputMethodContext);
459 // Enables the vertical scrolling after the text input has been enabled.
460 controller->SetVerticalScrollEnabled(true);
462 // Disables the horizontal scrolling.
463 controller->SetHorizontalScrollEnabled(false);
465 // No maximum number of characters.
466 controller->SetMaximumNumberOfCharacters(std::numeric_limits<Length>::max());
468 // Disable the text elide.
469 controller->SetTextElideEnabled(false);
471 // Disable match system language direction
472 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
475 Vector<FontDescriptionRun> CreateSingleFontDescription(
476 const CharacterRun& characterRun,
477 const std::string fontFamilyName,
478 const FontWeight weight,
479 const FontWidth width,
480 const FontSlant slant,
481 const PointSize26Dot6 size,
482 const bool familyDefined,
483 const bool weightDefined,
484 const bool widthDefined,
485 const bool slantDefined,
486 const bool sizeDefined)
488 FontDescriptionRun fontDescriptionRun =
503 fontDescriptionRun.familyLength = fontFamilyName.size();
504 fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
505 memcpy(fontDescriptionRun.familyName, fontFamilyName.c_str(), fontDescriptionRun.familyLength);
507 Vector<FontDescriptionRun> fontDescriptionRuns;
508 fontDescriptionRuns.PushBack(fontDescriptionRun);
510 return fontDescriptionRuns;
515 } // namespace Toolkit