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 <dali-toolkit/internal/text/logical-model-impl.h>
22 #include <dali-toolkit/internal/text/bounded-paragraph-helper-functions.h>
23 #include <dali-toolkit/internal/text/input-style.h>
24 #include <dali-toolkit/internal/text/text-run-container.h>
32 void FreeFontFamilyNames(Vector<FontDescriptionRun>& fontDescriptionRuns)
34 for(Vector<FontDescriptionRun>::Iterator it = fontDescriptionRuns.Begin(),
35 endIt = fontDescriptionRuns.End();
39 delete[](*it).familyName;
42 fontDescriptionRuns.Clear();
45 void FreeEmbeddedItems(Vector<EmbeddedItem>& embeddedItem)
47 for(Vector<EmbeddedItem>::Iterator it = embeddedItem.Begin(),
48 endIt = embeddedItem.End();
52 EmbeddedItem& item = *it;
59 void FreeAnchors(Vector<Anchor>& anchors)
61 for(auto&& anchor : anchors)
69 LogicalModelPtr LogicalModel::New()
71 return LogicalModelPtr(new LogicalModel());
74 Script LogicalModel::GetScript(CharacterIndex characterIndex) const
76 // If this operation is too slow, consider a binary search.
78 const ScriptRun* const scriptRunBuffer = mScriptRuns.Begin();
79 for(Length index = 0u, length = mScriptRuns.Count(); index < length; ++index)
81 const ScriptRun* const scriptRun = scriptRunBuffer + index;
83 if((scriptRun->characterRun.characterIndex <= characterIndex) &&
84 (characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters))
86 return scriptRun->script;
90 return TextAbstraction::UNKNOWN;
93 CharacterDirection LogicalModel::GetCharacterDirection(CharacterIndex characterIndex) const
95 if(characterIndex >= mCharacterDirections.Count())
97 // The model has no right to left characters, so the vector of directions is void.
101 return *(mCharacterDirections.Begin() + characterIndex);
104 CharacterIndex LogicalModel::GetLogicalCursorIndex(CharacterIndex visualCursorIndex)
106 // The character's directions buffer.
107 const CharacterDirection* const modelCharacterDirections = mCharacterDirections.Begin();
109 // The bidirectional line info.
110 const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
112 // Whether the paragraph starts with a right to left character.
113 const bool isRightToLeftParagraph = bidirectionalLineInfo->direction;
115 // The total number of characters of the line.
116 const Length lastCharacterIndex = bidirectionalLineInfo->characterRun.characterIndex + bidirectionalLineInfo->characterRun.numberOfCharacters;
118 CharacterIndex logicalCursorIndex = 0u;
120 if(bidirectionalLineInfo->characterRun.characterIndex == visualCursorIndex)
122 if(isRightToLeftParagraph)
124 logicalCursorIndex = lastCharacterIndex;
126 else // else logical position is the first of the line.
128 logicalCursorIndex = bidirectionalLineInfo->characterRun.characterIndex;
131 else if(lastCharacterIndex == visualCursorIndex)
133 if(isRightToLeftParagraph)
135 logicalCursorIndex = bidirectionalLineInfo->characterRun.characterIndex;
137 else // else logical position is the number of characters.
139 logicalCursorIndex = lastCharacterIndex;
144 // Get the character indexed by index - 1 and index
145 // and calculate the logical position according the directions of
146 // both characters and the direction of the paragraph.
148 const CharacterIndex previousVisualCursorIndex = visualCursorIndex - 1u;
149 const CharacterIndex previousLogicalCursorIndex = *(bidirectionalLineInfo->visualToLogicalMap + static_cast<std::size_t>(previousVisualCursorIndex - bidirectionalLineInfo->characterRun.characterIndex)) + bidirectionalLineInfo->characterRun.characterIndex;
150 const CharacterIndex currentLogicalCursorIndex = *(bidirectionalLineInfo->visualToLogicalMap + static_cast<std::size_t>(visualCursorIndex - bidirectionalLineInfo->characterRun.characterIndex)) + bidirectionalLineInfo->characterRun.characterIndex;
152 const CharacterDirection previousCharacterDirection = *(modelCharacterDirections + previousLogicalCursorIndex);
153 const CharacterDirection currentCharacterDirection = *(modelCharacterDirections + currentLogicalCursorIndex);
155 if(previousCharacterDirection == currentCharacterDirection)
157 // Both glyphs have the same direction.
158 if(previousCharacterDirection)
160 logicalCursorIndex = previousLogicalCursorIndex;
164 logicalCursorIndex = currentLogicalCursorIndex;
169 if(isRightToLeftParagraph)
171 if(currentCharacterDirection)
173 logicalCursorIndex = currentLogicalCursorIndex + 1u;
177 logicalCursorIndex = previousLogicalCursorIndex;
182 if(previousCharacterDirection)
184 logicalCursorIndex = currentLogicalCursorIndex;
188 logicalCursorIndex = previousLogicalCursorIndex + 1u;
194 return logicalCursorIndex;
197 CharacterIndex LogicalModel::GetLogicalCharacterIndex(CharacterIndex visualCharacterIndex)
199 // The bidirectional line info.
200 const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
202 return *(bidirectionalLineInfo->visualToLogicalMap + static_cast<std::size_t>(visualCharacterIndex - bidirectionalLineInfo->characterRun.characterIndex)) + bidirectionalLineInfo->characterRun.characterIndex;
205 bool LogicalModel::FetchBidirectionalLineInfo(CharacterIndex characterIndex)
207 // The number of bidirectional lines.
208 const Length numberOfBidirectionalLines = mBidirectionalLineInfo.Count();
210 if(0u == numberOfBidirectionalLines)
212 // If there is no bidirectional info.
216 // Find the bidi line where the character is laid-out.
218 const BidirectionalLineInfoRun* const bidirectionalLineInfoBuffer = mBidirectionalLineInfo.Begin();
220 // Check first if the character is in the previously fetched line.
222 BidirectionalLineRunIndex bidiLineIndex = 0u;
223 CharacterIndex lastCharacterOfRightToLeftRun = 0u;
224 if(mBidirectionalLineIndex < numberOfBidirectionalLines)
226 const BidirectionalLineInfoRun& bidiLineRun = *(bidirectionalLineInfoBuffer + mBidirectionalLineIndex);
228 const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
229 if((bidiLineRun.characterRun.characterIndex <= characterIndex) &&
230 (characterIndex < lastCharacterOfRunPlusOne))
232 // The character is in the previously fetched bidi line.
237 // The character is not in the previously fetched line.
238 // Set the bidi line index from where to start the fetch.
240 if(characterIndex < bidiLineRun.characterRun.characterIndex)
242 // Start the fetch from the beginning.
247 // Start the fetch from the next line.
248 bidiLineIndex = mBidirectionalLineIndex + 1u;
249 lastCharacterOfRightToLeftRun = lastCharacterOfRunPlusOne - 1u;
254 // The character has not been found in the previously fetched bidi line.
255 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLineInfoBuffer + bidiLineIndex,
256 endIt = mBidirectionalLineInfo.End();
258 ++it, ++bidiLineIndex)
260 const BidirectionalLineInfoRun& bidiLineRun = *it;
262 if((lastCharacterOfRightToLeftRun < characterIndex) &&
263 (characterIndex < bidiLineRun.characterRun.characterIndex))
265 // The character is not inside a bidi line.
269 const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
270 lastCharacterOfRightToLeftRun = lastCharacterOfRunPlusOne - 1u;
271 if((bidiLineRun.characterRun.characterIndex <= characterIndex) &&
272 (characterIndex < lastCharacterOfRunPlusOne))
274 // Bidi line found. Fetch the line.
275 mBidirectionalLineIndex = bidiLineIndex;
283 BidirectionalLineRunIndex LogicalModel::GetBidirectionalLineInfo() const
285 return mBidirectionalLineIndex;
288 void LogicalModel::UpdateTextStyleRuns(CharacterIndex index, int numberOfCharacters)
290 const Length totalNumberOfCharacters = mText.Count();
292 // Process the color runs.
293 Vector<ColorRun> removedColorRuns;
294 UpdateCharacterRuns<ColorRun>(index,
296 totalNumberOfCharacters,
300 // This is needed until now for underline tag in mark-up processor
301 // Process the underlined runs.
302 Vector<UnderlinedCharacterRun> removedUnderlinedCharacterRuns;
303 UpdateCharacterRuns<UnderlinedCharacterRun>(index,
305 totalNumberOfCharacters,
306 mUnderlinedCharacterRuns,
307 removedUnderlinedCharacterRuns);
309 // Process the strikethrough runs.
310 Vector<StrikethroughCharacterRun> removedStrikethroughCharacterRuns;
311 UpdateCharacterRuns<StrikethroughCharacterRun>(index,
313 totalNumberOfCharacters,
314 mStrikethroughCharacterRuns,
315 removedStrikethroughCharacterRuns);
317 // Process the background color runs.
318 Vector<ColorRun> removedBackgroundColorRuns;
319 UpdateCharacterRuns<ColorRun>(index,
321 totalNumberOfCharacters,
322 mBackgroundColorRuns,
323 removedBackgroundColorRuns);
325 // Process the font description runs.
326 Vector<FontDescriptionRun> removedFontDescriptionRuns;
327 UpdateCharacterRuns<FontDescriptionRun>(index,
329 totalNumberOfCharacters,
330 mFontDescriptionRuns,
331 removedFontDescriptionRuns);
333 // Free memory allocated for the font family name.
334 FreeFontFamilyNames(removedFontDescriptionRuns);
336 // Process the bounded paragraph runs
337 MergeBoundedParagraphRunsWhenRemoveCharacters(mText,
340 mBoundedParagraphRuns);
342 Vector<BoundedParagraphRun> removedBoundedParagraphRuns;
343 UpdateCharacterRuns<BoundedParagraphRun>(index,
345 totalNumberOfCharacters,
346 mBoundedParagraphRuns,
347 removedBoundedParagraphRuns);
349 Vector<CharacterSpacingCharacterRun> removedCharacterSpacingCharacterRuns;
350 UpdateCharacterRuns<CharacterSpacingCharacterRun>(index,
352 totalNumberOfCharacters,
353 mCharacterSpacingCharacterRuns,
354 removedCharacterSpacingCharacterRuns);
357 void LogicalModel::RetrieveStyle(CharacterIndex index, InputStyle& style)
359 unsigned int runIndex = 0u;
361 // Set the text color.
362 bool colorOverriden = false;
363 unsigned int colorIndex = 0u;
364 const ColorRun* const colorRunsBuffer = mColorRuns.Begin();
365 for(Vector<ColorRun>::ConstIterator it = colorRunsBuffer,
366 endIt = mColorRuns.End();
370 const ColorRun& colorRun = *it;
372 if((colorRun.characterRun.characterIndex <= index) &&
373 (index < colorRun.characterRun.characterIndex + colorRun.characterRun.numberOfCharacters))
375 colorIndex = runIndex;
376 colorOverriden = true;
380 // Set the text's color if it's overriden.
383 style.textColor = (*(colorRunsBuffer + colorIndex)).color;
384 style.isDefaultColor = false;
387 // Reset the run index.
390 // Set the font's parameters.
391 bool nameOverriden = false;
392 bool weightOverriden = false;
393 bool widthOverriden = false;
394 bool slantOverriden = false;
395 bool sizeOverriden = false;
396 unsigned int nameIndex = 0u;
397 unsigned int weightIndex = 0u;
398 unsigned int widthIndex = 0u;
399 unsigned int slantIndex = 0u;
400 unsigned int sizeIndex = 0u;
401 const FontDescriptionRun* const fontDescriptionRunsBuffer = mFontDescriptionRuns.Begin();
402 for(Vector<FontDescriptionRun>::ConstIterator it = fontDescriptionRunsBuffer,
403 endIt = mFontDescriptionRuns.End();
407 const FontDescriptionRun& fontDescriptionRun = *it;
409 if((fontDescriptionRun.characterRun.characterIndex <= index) &&
410 (index < fontDescriptionRun.characterRun.characterIndex + fontDescriptionRun.characterRun.numberOfCharacters))
412 if(fontDescriptionRun.familyDefined)
414 nameIndex = runIndex;
415 nameOverriden = true;
418 if(fontDescriptionRun.weightDefined)
420 weightIndex = runIndex;
421 weightOverriden = true;
424 if(fontDescriptionRun.widthDefined)
426 widthIndex = runIndex;
427 widthOverriden = true;
430 if(fontDescriptionRun.slantDefined)
432 slantIndex = runIndex;
433 slantOverriden = true;
436 if(fontDescriptionRun.sizeDefined)
438 sizeIndex = runIndex;
439 sizeOverriden = true;
444 // Set the font's family name if it's overriden.
447 const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + nameIndex);
449 style.familyName = std::string(fontDescriptionRun.familyName, fontDescriptionRun.familyLength);
450 style.isFamilyDefined = true;
453 // Set the font's weight if it's overriden.
456 const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + weightIndex);
458 style.weight = fontDescriptionRun.weight;
459 style.isWeightDefined = true;
462 // Set the font's width if it's overriden.
465 const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + widthIndex);
467 style.width = fontDescriptionRun.width;
468 style.isWidthDefined = true;
471 // Set the font's slant if it's overriden.
474 const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + slantIndex);
476 style.slant = fontDescriptionRun.slant;
477 style.isSlantDefined = true;
480 // Set the font's size if it's overriden.
483 const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + sizeIndex);
485 style.size = static_cast<float>(fontDescriptionRun.size) / 64.f;
486 style.isSizeDefined = true;
490 void LogicalModel::ClearFontDescriptionRuns()
492 FreeFontFamilyNames(mFontDescriptionRuns);
495 void LogicalModel::ClearStrikethroughRuns()
497 mStrikethroughCharacterRuns.Clear();
500 void LogicalModel::ClearUnderlineRuns()
502 mUnderlinedCharacterRuns.Clear();
505 void LogicalModel::CreateParagraphInfo(CharacterIndex startIndex,
506 Length numberOfCharacters)
508 const Length totalNumberOfCharacters = mLineBreakInfo.Count();
510 // Count the number of LINE_MUST_BREAK to reserve some space for the vector of paragraph's info.
511 Vector<CharacterIndex> paragraphs;
512 paragraphs.Reserve(numberOfCharacters);
513 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = mLineBreakInfo.Begin();
514 const CharacterIndex lastCharacterIndexPlusOne = startIndex + numberOfCharacters;
515 for(Length index = startIndex; index < lastCharacterIndexPlusOne; ++index)
517 if(TextAbstraction::LINE_MUST_BREAK == *(lineBreakInfoBuffer + index))
519 paragraphs.PushBack(index);
523 // Whether the current paragraphs are updated or set from scratch.
524 const bool updateCurrentParagraphs = numberOfCharacters < totalNumberOfCharacters;
526 // Reserve space for current paragraphs plus new ones.
527 const Length numberOfNewParagraphs = paragraphs.Count();
528 const Length totalNumberOfParagraphs = mParagraphInfo.Count() + numberOfNewParagraphs;
529 mParagraphInfo.Resize(totalNumberOfParagraphs);
531 ParagraphRun* paragraphInfoBuffer = NULL;
532 Vector<ParagraphRun> newParagraphs;
534 if(updateCurrentParagraphs)
536 newParagraphs.Resize(numberOfNewParagraphs);
537 paragraphInfoBuffer = newParagraphs.Begin();
541 paragraphInfoBuffer = mParagraphInfo.Begin();
544 // Find where to insert the new paragraphs.
545 ParagraphRunIndex paragraphIndex = 0u;
546 CharacterIndex firstIndex = startIndex;
548 if(updateCurrentParagraphs)
550 for(Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
551 endIt = mParagraphInfo.Begin() + static_cast<std::size_t>(totalNumberOfParagraphs - numberOfNewParagraphs);
555 const ParagraphRun& paragraph(*it);
557 if(startIndex < paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters)
559 firstIndex = paragraph.characterRun.characterIndex;
567 // Create the paragraph info.
568 ParagraphRunIndex newParagraphIndex = 0u;
569 for(Vector<CharacterIndex>::ConstIterator it = paragraphs.Begin(),
570 endIt = paragraphs.End();
572 ++it, ++newParagraphIndex)
574 const CharacterIndex index = *it;
576 ParagraphRun& paragraph = *(paragraphInfoBuffer + newParagraphIndex);
577 paragraph.characterRun.characterIndex = firstIndex;
578 paragraph.characterRun.numberOfCharacters = 1u + index - firstIndex;
580 firstIndex += paragraph.characterRun.numberOfCharacters;
583 // Insert the new paragraphs.
584 if(updateCurrentParagraphs)
586 mParagraphInfo.Insert(mParagraphInfo.Begin() + paragraphIndex,
587 newParagraphs.Begin(),
588 newParagraphs.End());
590 mParagraphInfo.Resize(totalNumberOfParagraphs);
592 // Update the next paragraph indices.
593 for(Vector<ParagraphRun>::Iterator it = mParagraphInfo.Begin() + paragraphIndex + newParagraphs.Count(),
594 endIt = mParagraphInfo.End();
598 ParagraphRun& paragraph(*it);
600 paragraph.characterRun.characterIndex += numberOfCharacters;
605 void LogicalModel::FindParagraphs(CharacterIndex index,
606 Length numberOfCharacters,
607 Vector<ParagraphRunIndex>& paragraphs)
609 // Reserve som space for the paragraph indices.
610 paragraphs.Reserve(mParagraphInfo.Count());
612 // Traverse the paragraphs to find which ones contain the given characters.
613 ParagraphRunIndex paragraphIndex = 0u;
614 for(Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
615 endIt = mParagraphInfo.End();
617 ++it, ++paragraphIndex)
619 const ParagraphRun& paragraph(*it);
621 if((paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters > index) &&
622 (paragraph.characterRun.characterIndex < index + numberOfCharacters))
624 paragraphs.PushBack(paragraphIndex);
629 Length LogicalModel::GetNumberOfBoundedParagraphRuns() const
631 return mBoundedParagraphRuns.Count();
634 const Vector<BoundedParagraphRun>& LogicalModel::GetBoundedParagraphRuns() const
636 return mBoundedParagraphRuns;
639 Length LogicalModel::GetNumberOfCharacterSpacingCharacterRuns() const
641 return mCharacterSpacingCharacterRuns.Count();
644 const Vector<CharacterSpacingCharacterRun>& LogicalModel::GetCharacterSpacingCharacterRuns() const
646 return mCharacterSpacingCharacterRuns;
649 void LogicalModel::ClearEmbeddedImages()
651 FreeEmbeddedItems(mEmbeddedItems);
654 void LogicalModel::ClearAnchors()
656 FreeAnchors(mAnchors);
659 LogicalModel::~LogicalModel()
661 ClearFontDescriptionRuns();
662 ClearEmbeddedImages();
665 LogicalModel::LogicalModel()
666 : mBidirectionalLineIndex(0u),
667 mSpannedTextPlaced(false),
668 mUnderlineRunsUpdated(false),
669 mCharacterSpacingRunsUpdated(false),
670 mStrikethroughRunsUpdated(false)
676 } // namespace Toolkit