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.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>
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,
107 textModel = Model::New(); ///< Pointer to the text's model.
108 LogicalModelPtr logicalModel = textModel->mLogicalModel;
109 VisualModelPtr visualModel = textModel->mVisualModel;
111 MarkupProcessData markupProcessData(logicalModel->mColorRuns,
112 logicalModel->mFontDescriptionRuns,
113 logicalModel->mEmbeddedItems,
114 logicalModel->mAnchors,
115 logicalModel->mUnderlinedCharacterRuns,
116 logicalModel->mBackgroundColorRuns,
117 logicalModel->mStrikethroughCharacterRuns);
119 Length textSize = 0u;
120 const uint8_t* utf8 = NULL;
121 if(markupProcessorEnabled)
123 ProcessMarkupString(text, markupProcessData);
124 textSize = markupProcessData.markupProcessedText.size();
126 // This is a bit horrible but std::string returns a (signed) char*
127 utf8 = reinterpret_cast<const uint8_t*>(markupProcessData.markupProcessedText.c_str());
131 textSize = text.size();
133 // This is a bit horrible but std::string returns a (signed) char*
134 utf8 = reinterpret_cast<const uint8_t*>(text.c_str());
138 textModel->mElideEnabled = ellipsisEnabled;
139 textModel->mVisualModel->SetTextElideEnabled(ellipsisEnabled);
140 textModel->mEllipsisPosition = ellipsisPosition;
141 textModel->mVisualModel->SetEllipsisPosition(ellipsisPosition);
143 // 1) Convert to utf32
144 Vector<Character>& utf32Characters = logicalModel->mText;
145 utf32Characters.Resize(textSize);
147 // Transform a text array encoded in utf8 into an array encoded in utf32.
148 // It returns the actual number of characters.
149 Length characterCount = Utf8ToUtf32(utf8, textSize, utf32Characters.Begin());
150 utf32Characters.Resize(characterCount);
152 // 2) Set the break and paragraph info.
153 Vector<LineBreakInfo>& lineBreakInfo = logicalModel->mLineBreakInfo;
154 lineBreakInfo.Resize(characterCount);
156 SetLineBreakInfo(utf32Characters,
161 if(0u == characterCount)
163 // Nothing else to do if the number of characters is zero.
167 textModel->mLineWrapMode = wrapMode;
169 if(textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
170 textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
172 CharacterIndex end = characterCount;
173 LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
175 for(CharacterIndex index = 0; index < end; index++)
177 CharacterIndex wordEnd = index;
178 while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
183 if((wordEnd + 1) == end) // add last char
188 Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
190 for(CharacterIndex i = 0; i < (wordEnd - index); i++)
194 *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
202 // 3) Set the script info.
203 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
205 Vector<ScriptRun>& scripts = logicalModel->mScriptRuns;
206 multilanguageSupport.SetScripts(utf32Characters,
211 // 4) Set the font info
212 Vector<FontDescriptionRun>& fontDescriptionRuns = logicalModel->mFontDescriptionRuns;
213 fontDescriptionRuns = fontDescriptions;
214 Vector<FontRun>& validFonts = logicalModel->mFontRuns;
216 // The default font description.
217 TextAbstraction::FontDescription fontDescription;
219 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
220 fontClient.SetDpi(96u, 96u);
222 // Validates the fonts. If there is a character with no assigned font it sets a default one.
223 // After this call, fonts are validated.
224 multilanguageSupport.ValidateFonts(utf32Characters,
228 TextAbstraction::FontClient::DEFAULT_POINT_SIZE,
233 // 5) Set the bidirectional info per paragraph.
234 Vector<Character> mirroredUtf32Characters;
235 bool textMirrored = false;
237 // Reserve some space for the vector of paragraph's bidirectional info.
238 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo;
240 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
241 SetBidirectionalInfo(utf32Characters,
248 // Create the paragraph info.
249 logicalModel->CreateParagraphInfo(0u,
252 // 6) Set character directions.
253 Vector<CharacterDirection>& characterDirections = logicalModel->mCharacterDirections;
254 if(0u != bidirectionalInfo.Count())
256 // Only set the character directions if there is right to left characters.
257 GetCharactersDirection(bidirectionalInfo,
261 characterDirections);
263 // This paragraph has right to left text. Some characters may need to be mirrored.
264 textMirrored = GetMirroredText(utf32Characters,
269 mirroredUtf32Characters);
273 // There is no right to left characters. Clear the directions vector.
274 characterDirections.Clear();
277 // 7) Shape the text.
279 Vector<GlyphInfo>& glyphs = visualModel->mGlyphs;
280 Vector<CharacterIndex>& glyphsToCharactersMap = visualModel->mGlyphsToCharacters;
281 Vector<Length>& charactersPerGlyph = visualModel->mCharactersPerGlyph;
282 Vector<GlyphIndex> newParagraphGlyphs;
284 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
286 ShapeText(textToShape,
294 glyphsToCharactersMap,
298 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
299 visualModel->CreateGlyphsPerCharacterTable(0u, 0u, characterCount);
300 visualModel->CreateCharacterToGlyphTable(0u, 0u, characterCount);
302 const Length numberOfGlyphs = glyphs.Count();
304 // 8) Get the glyph metrics
305 metrics = Metrics::New(fontClient);
307 GlyphInfo* glyphsBuffer = glyphs.Begin();
308 metrics->GetGlyphMetrics(glyphsBuffer, numberOfGlyphs);
310 // Update the width and advance of all new paragraph characters.
311 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(),
312 endIt = newParagraphGlyphs.End();
316 const GlyphIndex index = *it;
317 GlyphInfo& glyph = *(glyphsBuffer + index);
319 glyph.xBearing = 0.f;
324 // 9) Layout the text
325 Layout::Engine layoutEngine;
326 layoutEngine.SetMetrics(metrics);
327 layoutEngine.SetLayout(Layout::Engine::MULTI_LINE_BOX);
328 layoutEngine.SetDefaultLineSpacing(lineSpacing);
330 // Set the layout parameters.
331 textModel->mHorizontalAlignment = Text::HorizontalAlignment::BEGIN;
332 textModel->mIgnoreSpacesAfterText = true;
333 Layout::Parameters layoutParameters(textArea,
336 Vector<LineRun>& lines = visualModel->mLines;
338 Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
339 glyphPositions.Resize(numberOfGlyphs);
341 layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph(*(utf32Characters.Begin() + (characterCount - 1u)));
343 // The initial glyph and the number of glyphs to layout.
344 layoutParameters.startGlyphIndex = 0u;
345 layoutParameters.numberOfGlyphs = numberOfGlyphs;
346 layoutParameters.startLineIndex = 0u;
347 layoutParameters.estimatedNumberOfLines = logicalModel->mParagraphInfo.Count();
349 bool isAutoScroll = false;
350 layoutEngine.LayoutText(layoutParameters,
358 float alignmentOffset = 0.f;
359 layoutEngine.Align(textArea,
362 Text::HorizontalAlignment::BEGIN,
365 Dali::LayoutDirection::LEFT_TO_RIGHT,
370 void ConfigureTextLabel(ControllerPtr controller)
372 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
373 fontClient.SetDpi(93u, 93u);
375 // Set the text layout as multi-line.
376 controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
378 // Set cursor's width to zero.
379 controller->GetLayoutEngine().SetCursorWidth(0);
381 InputMethodContext inputMethodContext = InputMethodContext::New();
382 // Disables the text input.
383 controller->EnableTextInput(NULL, inputMethodContext);
385 // Disables the vertical scrolling.
386 controller->SetVerticalScrollEnabled(false);
388 // Disables the horizontal scrolling.
389 controller->SetHorizontalScrollEnabled(false);
391 // Enable the text elide.
392 controller->SetTextElideEnabled(true);
394 // Disable match system language direction
395 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
398 void ConfigureTextField(ControllerPtr controller)
400 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
401 fontClient.SetDpi(93u, 93u);
403 // Creates a decorator.
404 Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
407 // Set the text layout as multi-line.
408 controller->GetLayoutEngine().SetLayout(Layout::Engine::SINGLE_LINE_BOX);
410 InputMethodContext inputMethodContext = InputMethodContext::New();
411 // Enables the text input.
412 controller->EnableTextInput(decorator, inputMethodContext);
414 // Enables the vertical scrolling after the text input has been enabled.
415 controller->SetVerticalScrollEnabled(false);
417 // Disables the horizontal scrolling.
418 controller->SetHorizontalScrollEnabled(true);
420 // No maximum number of characters.
421 controller->SetMaximumNumberOfCharacters(50u);
423 // Disable the text elide.
424 controller->SetTextElideEnabled(false);
426 // Disable match system language direction
427 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
430 void ConfigureTextEditor(ControllerPtr controller)
432 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
433 fontClient.SetDpi(93u, 93u);
435 // Creates a decorator.
436 Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
439 // Set the text layout as multi-line.
440 controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
442 InputMethodContext inputMethodContext = InputMethodContext::New();
443 // Enables the text input.
444 controller->EnableTextInput(decorator, inputMethodContext);
446 // Enables the vertical scrolling after the text input has been enabled.
447 controller->SetVerticalScrollEnabled(true);
449 // Disables the horizontal scrolling.
450 controller->SetHorizontalScrollEnabled(false);
452 // No maximum number of characters.
453 controller->SetMaximumNumberOfCharacters(std::numeric_limits<Length>::max());
455 // Disable the text elide.
456 controller->SetTextElideEnabled(false);
458 // Disable match system language direction
459 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
462 Vector<FontDescriptionRun> CreateSingleFontDescription(
463 const CharacterRun& characterRun,
464 const std::string fontFamilyName,
465 const FontWeight weight,
466 const FontWidth width,
467 const FontSlant slant,
468 const PointSize26Dot6 size,
469 const bool familyDefined,
470 const bool weightDefined,
471 const bool widthDefined,
472 const bool slantDefined,
473 const bool sizeDefined)
475 FontDescriptionRun fontDescriptionRun =
490 fontDescriptionRun.familyLength = fontFamilyName.size();
491 fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
492 memcpy(fontDescriptionRun.familyName, fontFamilyName.c_str(), fontDescriptionRun.familyLength);
494 Vector<FontDescriptionRun> fontDescriptionRuns;
495 fontDescriptionRuns.PushBack(fontDescriptionRun);
497 return fontDescriptionRuns;
502 } // namespace Toolkit