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,
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);
120 Length textSize = 0u;
121 const uint8_t* utf8 = NULL;
122 if(markupProcessorEnabled)
124 ProcessMarkupString(text, markupProcessData);
125 textSize = markupProcessData.markupProcessedText.size();
127 // This is a bit horrible but std::string returns a (signed) char*
128 utf8 = reinterpret_cast<const uint8_t*>(markupProcessData.markupProcessedText.c_str());
132 textSize = text.size();
134 // This is a bit horrible but std::string returns a (signed) char*
135 utf8 = reinterpret_cast<const uint8_t*>(text.c_str());
139 textModel->mElideEnabled = ellipsisEnabled;
140 textModel->mVisualModel->SetTextElideEnabled(ellipsisEnabled);
141 textModel->mEllipsisPosition = ellipsisPosition;
142 textModel->mVisualModel->SetEllipsisPosition(ellipsisPosition);
144 // 1) Convert to utf32
145 Vector<Character>& utf32Characters = logicalModel->mText;
146 utf32Characters.Resize(textSize);
148 // Transform a text array encoded in utf8 into an array encoded in utf32.
149 // It returns the actual number of characters.
150 Length characterCount = Utf8ToUtf32(utf8, textSize, utf32Characters.Begin());
151 utf32Characters.Resize(characterCount);
153 // 2) Set the break and paragraph info.
154 Vector<LineBreakInfo>& lineBreakInfo = logicalModel->mLineBreakInfo;
155 lineBreakInfo.Resize(characterCount);
157 SetLineBreakInfo(utf32Characters,
162 if(0u == characterCount)
164 // Nothing else to do if the number of characters is zero.
168 textModel->mLineWrapMode = wrapMode;
170 if(textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
171 textModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
173 CharacterIndex end = characterCount;
174 LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
176 for(CharacterIndex index = 0; index < end; index++)
178 CharacterIndex wordEnd = index;
179 while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
184 if((wordEnd + 1) == end) // add last char
189 Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
191 for(CharacterIndex i = 0; i < (wordEnd - index); i++)
195 *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
203 // 3) Set the script info.
204 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
206 Vector<ScriptRun>& scripts = logicalModel->mScriptRuns;
207 multilanguageSupport.SetScripts(utf32Characters,
212 // 4) Set the font info
213 Vector<FontDescriptionRun>& fontDescriptionRuns = logicalModel->mFontDescriptionRuns;
214 fontDescriptionRuns = fontDescriptions;
215 Vector<FontRun>& validFonts = logicalModel->mFontRuns;
217 // The default font description.
218 TextAbstraction::FontDescription fontDescription;
220 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
221 fontClient.SetDpi(96u, 96u);
223 // Validates the fonts. If there is a character with no assigned font it sets a default one.
224 // After this call, fonts are validated.
225 multilanguageSupport.ValidateFonts(utf32Characters,
229 TextAbstraction::FontClient::DEFAULT_POINT_SIZE,
234 // 5) Set the bidirectional info per paragraph.
235 Vector<Character> mirroredUtf32Characters;
236 bool textMirrored = false;
238 // Reserve some space for the vector of paragraph's bidirectional info.
239 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = logicalModel->mBidirectionalParagraphInfo;
241 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
242 SetBidirectionalInfo(utf32Characters,
249 // Create the paragraph info.
250 logicalModel->CreateParagraphInfo(0u,
253 // 6) Set character directions.
254 Vector<CharacterDirection>& characterDirections = logicalModel->mCharacterDirections;
255 if(0u != bidirectionalInfo.Count())
257 // Only set the character directions if there is right to left characters.
258 GetCharactersDirection(bidirectionalInfo,
262 characterDirections);
264 // This paragraph has right to left text. Some characters may need to be mirrored.
265 textMirrored = GetMirroredText(utf32Characters,
270 mirroredUtf32Characters);
274 // There is no right to left characters. Clear the directions vector.
275 characterDirections.Clear();
278 // 7) Shape the text.
280 Vector<GlyphInfo>& glyphs = visualModel->mGlyphs;
281 Vector<CharacterIndex>& glyphsToCharactersMap = visualModel->mGlyphsToCharacters;
282 Vector<Length>& charactersPerGlyph = visualModel->mCharactersPerGlyph;
283 Vector<GlyphIndex> newParagraphGlyphs;
285 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
287 ShapeText(textToShape,
295 glyphsToCharactersMap,
299 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
300 visualModel->CreateGlyphsPerCharacterTable(0u, 0u, characterCount);
301 visualModel->CreateCharacterToGlyphTable(0u, 0u, characterCount);
303 visualModel->SetCharacterSpacing(characterSpacing);
305 const Length numberOfGlyphs = glyphs.Count();
307 // 8) Get the glyph metrics
308 metrics = Metrics::New(fontClient);
310 GlyphInfo* glyphsBuffer = glyphs.Begin();
311 metrics->GetGlyphMetrics(glyphsBuffer, numberOfGlyphs);
313 // Update the width and advance of all new paragraph characters.
314 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(),
315 endIt = newParagraphGlyphs.End();
319 const GlyphIndex index = *it;
320 GlyphInfo& glyph = *(glyphsBuffer + index);
322 glyph.xBearing = 0.f;
327 // 9) Layout the text
328 Layout::Engine layoutEngine;
329 layoutEngine.SetMetrics(metrics);
330 layoutEngine.SetLayout(Layout::Engine::MULTI_LINE_BOX);
331 layoutEngine.SetDefaultLineSpacing(lineSpacing);
333 // Set the layout parameters.
334 textModel->mHorizontalAlignment = Text::HorizontalAlignment::BEGIN;
335 textModel->mIgnoreSpacesAfterText = true;
336 Layout::Parameters layoutParameters(textArea,
339 Vector<LineRun>& lines = visualModel->mLines;
341 Vector<Vector2>& glyphPositions = visualModel->mGlyphPositions;
342 glyphPositions.Resize(numberOfGlyphs);
344 layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph(*(utf32Characters.Begin() + (characterCount - 1u)));
346 // The initial glyph and the number of glyphs to layout.
347 layoutParameters.startGlyphIndex = 0u;
348 layoutParameters.numberOfGlyphs = numberOfGlyphs;
349 layoutParameters.startLineIndex = 0u;
350 layoutParameters.estimatedNumberOfLines = logicalModel->mParagraphInfo.Count();
352 bool isAutoScroll = false;
353 layoutEngine.LayoutText(layoutParameters,
361 float alignmentOffset = 0.f;
362 layoutEngine.Align(textArea,
365 Text::HorizontalAlignment::BEGIN,
368 Dali::LayoutDirection::LEFT_TO_RIGHT,
373 void ConfigureTextLabel(ControllerPtr controller)
375 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
376 fontClient.SetDpi(93u, 93u);
378 // Set the text layout as multi-line.
379 controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
381 // Set cursor's width to zero.
382 controller->GetLayoutEngine().SetCursorWidth(0);
384 InputMethodContext inputMethodContext = InputMethodContext::New();
385 // Disables the text input.
386 controller->EnableTextInput(NULL, inputMethodContext);
388 // Disables the vertical scrolling.
389 controller->SetVerticalScrollEnabled(false);
391 // Disables the horizontal scrolling.
392 controller->SetHorizontalScrollEnabled(false);
394 // Enable the text elide.
395 controller->SetTextElideEnabled(true);
397 // Disable match system language direction
398 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
401 void ConfigureTextField(ControllerPtr controller)
403 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
404 fontClient.SetDpi(93u, 93u);
406 // Creates a decorator.
407 Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
410 // Set the text layout as multi-line.
411 controller->GetLayoutEngine().SetLayout(Layout::Engine::SINGLE_LINE_BOX);
413 InputMethodContext inputMethodContext = InputMethodContext::New();
414 // Enables the text input.
415 controller->EnableTextInput(decorator, inputMethodContext);
417 // Enables the vertical scrolling after the text input has been enabled.
418 controller->SetVerticalScrollEnabled(false);
420 // Disables the horizontal scrolling.
421 controller->SetHorizontalScrollEnabled(true);
423 // No maximum number of characters.
424 controller->SetMaximumNumberOfCharacters(50u);
426 // Disable the text elide.
427 controller->SetTextElideEnabled(false);
429 // Disable match system language direction
430 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
433 void ConfigureTextEditor(ControllerPtr controller)
435 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
436 fontClient.SetDpi(93u, 93u);
438 // Creates a decorator.
439 Text::DecoratorPtr decorator = Text::Decorator::New(*controller,
442 // Set the text layout as multi-line.
443 controller->GetLayoutEngine().SetLayout(Layout::Engine::MULTI_LINE_BOX);
445 InputMethodContext inputMethodContext = InputMethodContext::New();
446 // Enables the text input.
447 controller->EnableTextInput(decorator, inputMethodContext);
449 // Enables the vertical scrolling after the text input has been enabled.
450 controller->SetVerticalScrollEnabled(true);
452 // Disables the horizontal scrolling.
453 controller->SetHorizontalScrollEnabled(false);
455 // No maximum number of characters.
456 controller->SetMaximumNumberOfCharacters(std::numeric_limits<Length>::max());
458 // Disable the text elide.
459 controller->SetTextElideEnabled(false);
461 // Disable match system language direction
462 controller->SetMatchLayoutDirection(DevelText::MatchLayoutDirection::CONTENTS);
465 Vector<FontDescriptionRun> CreateSingleFontDescription(
466 const CharacterRun& characterRun,
467 const std::string fontFamilyName,
468 const FontWeight weight,
469 const FontWidth width,
470 const FontSlant slant,
471 const PointSize26Dot6 size,
472 const bool familyDefined,
473 const bool weightDefined,
474 const bool widthDefined,
475 const bool slantDefined,
476 const bool sizeDefined)
478 FontDescriptionRun fontDescriptionRun =
493 fontDescriptionRun.familyLength = fontFamilyName.size();
494 fontDescriptionRun.familyName = new char[fontDescriptionRun.familyLength];
495 memcpy(fontDescriptionRun.familyName, fontFamilyName.c_str(), fontDescriptionRun.familyLength);
497 Vector<FontDescriptionRun> fontDescriptionRuns;
498 fontDescriptionRuns.PushBack(fontDescriptionRun);
500 return fontDescriptionRuns;
505 } // namespace Toolkit