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/input-style.h>
23 #include <dali-toolkit/internal/text/text-run-container.h>
31 void FreeFontFamilyNames(Vector<FontDescriptionRun>& fontDescriptionRuns)
33 for(Vector<FontDescriptionRun>::Iterator it = fontDescriptionRuns.Begin(),
34 endIt = fontDescriptionRuns.End();
38 delete[](*it).familyName;
41 fontDescriptionRuns.Clear();
44 void FreeEmbeddedItems(Vector<EmbeddedItem>& embeddedItem)
46 for(Vector<EmbeddedItem>::Iterator it = embeddedItem.Begin(),
47 endIt = embeddedItem.End();
51 EmbeddedItem& item = *it;
58 void FreeAnchors(Vector<Anchor>& anchors)
60 for(auto&& anchor : anchors)
68 LogicalModelPtr LogicalModel::New()
70 return LogicalModelPtr(new LogicalModel());
73 Script LogicalModel::GetScript(CharacterIndex characterIndex) const
75 // If this operation is too slow, consider a binary search.
77 const ScriptRun* const scriptRunBuffer = mScriptRuns.Begin();
78 for(Length index = 0u, length = mScriptRuns.Count(); index < length; ++index)
80 const ScriptRun* const scriptRun = scriptRunBuffer + index;
82 if((scriptRun->characterRun.characterIndex <= characterIndex) &&
83 (characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters))
85 return scriptRun->script;
89 return TextAbstraction::UNKNOWN;
92 CharacterDirection LogicalModel::GetCharacterDirection(CharacterIndex characterIndex) const
94 if(characterIndex >= mCharacterDirections.Count())
96 // The model has no right to left characters, so the vector of directions is void.
100 return *(mCharacterDirections.Begin() + characterIndex);
103 CharacterIndex LogicalModel::GetLogicalCursorIndex(CharacterIndex visualCursorIndex)
105 // The character's directions buffer.
106 const CharacterDirection* const modelCharacterDirections = mCharacterDirections.Begin();
108 // The bidirectional line info.
109 const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
111 // Whether the paragraph starts with a right to left character.
112 const bool isRightToLeftParagraph = bidirectionalLineInfo->direction;
114 // The total number of characters of the line.
115 const Length lastCharacterIndex = bidirectionalLineInfo->characterRun.characterIndex + bidirectionalLineInfo->characterRun.numberOfCharacters;
117 CharacterIndex logicalCursorIndex = 0u;
119 if(bidirectionalLineInfo->characterRun.characterIndex == visualCursorIndex)
121 if(isRightToLeftParagraph)
123 logicalCursorIndex = lastCharacterIndex;
125 else // else logical position is the first of the line.
127 logicalCursorIndex = bidirectionalLineInfo->characterRun.characterIndex;
130 else if(lastCharacterIndex == visualCursorIndex)
132 if(isRightToLeftParagraph)
134 logicalCursorIndex = bidirectionalLineInfo->characterRun.characterIndex;
136 else // else logical position is the number of characters.
138 logicalCursorIndex = lastCharacterIndex;
143 // Get the character indexed by index - 1 and index
144 // and calculate the logical position according the directions of
145 // both characters and the direction of the paragraph.
147 const CharacterIndex previousVisualCursorIndex = visualCursorIndex - 1u;
148 const CharacterIndex previousLogicalCursorIndex = *(bidirectionalLineInfo->visualToLogicalMap + previousVisualCursorIndex - bidirectionalLineInfo->characterRun.characterIndex) + bidirectionalLineInfo->characterRun.characterIndex;
149 const CharacterIndex currentLogicalCursorIndex = *(bidirectionalLineInfo->visualToLogicalMap + visualCursorIndex - bidirectionalLineInfo->characterRun.characterIndex) + bidirectionalLineInfo->characterRun.characterIndex;
151 const CharacterDirection previousCharacterDirection = *(modelCharacterDirections + previousLogicalCursorIndex);
152 const CharacterDirection currentCharacterDirection = *(modelCharacterDirections + currentLogicalCursorIndex);
154 if(previousCharacterDirection == currentCharacterDirection)
156 // Both glyphs have the same direction.
157 if(previousCharacterDirection)
159 logicalCursorIndex = previousLogicalCursorIndex;
163 logicalCursorIndex = currentLogicalCursorIndex;
168 if(isRightToLeftParagraph)
170 if(currentCharacterDirection)
172 logicalCursorIndex = currentLogicalCursorIndex + 1u;
176 logicalCursorIndex = previousLogicalCursorIndex;
181 if(previousCharacterDirection)
183 logicalCursorIndex = currentLogicalCursorIndex;
187 logicalCursorIndex = previousLogicalCursorIndex + 1u;
193 return logicalCursorIndex;
196 CharacterIndex LogicalModel::GetLogicalCharacterIndex(CharacterIndex visualCharacterIndex)
198 // The bidirectional line info.
199 const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
201 return *(bidirectionalLineInfo->visualToLogicalMap + visualCharacterIndex - bidirectionalLineInfo->characterRun.characterIndex) + bidirectionalLineInfo->characterRun.characterIndex;
204 bool LogicalModel::FetchBidirectionalLineInfo(CharacterIndex characterIndex)
206 // The number of bidirectional lines.
207 const Length numberOfBidirectionalLines = mBidirectionalLineInfo.Count();
209 if(0u == numberOfBidirectionalLines)
211 // If there is no bidirectional info.
215 // Find the bidi line where the character is laid-out.
217 const BidirectionalLineInfoRun* const bidirectionalLineInfoBuffer = mBidirectionalLineInfo.Begin();
219 // Check first if the character is in the previously fetched line.
221 BidirectionalLineRunIndex bidiLineIndex = 0u;
222 CharacterIndex lastCharacterOfRightToLeftRun = 0u;
223 if(mBidirectionalLineIndex < numberOfBidirectionalLines)
225 const BidirectionalLineInfoRun& bidiLineRun = *(bidirectionalLineInfoBuffer + mBidirectionalLineIndex);
227 const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
228 if((bidiLineRun.characterRun.characterIndex <= characterIndex) &&
229 (characterIndex < lastCharacterOfRunPlusOne))
231 // The character is in the previously fetched bidi line.
236 // The character is not in the previously fetched line.
237 // Set the bidi line index from where to start the fetch.
239 if(characterIndex < bidiLineRun.characterRun.characterIndex)
241 // Start the fetch from the beginning.
246 // Start the fetch from the next line.
247 bidiLineIndex = mBidirectionalLineIndex + 1u;
248 lastCharacterOfRightToLeftRun = lastCharacterOfRunPlusOne - 1u;
253 // The character has not been found in the previously fetched bidi line.
254 for(Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLineInfoBuffer + bidiLineIndex,
255 endIt = mBidirectionalLineInfo.End();
257 ++it, ++bidiLineIndex)
259 const BidirectionalLineInfoRun& bidiLineRun = *it;
261 if((lastCharacterOfRightToLeftRun < characterIndex) &&
262 (characterIndex < bidiLineRun.characterRun.characterIndex))
264 // The character is not inside a bidi line.
268 const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
269 lastCharacterOfRightToLeftRun = lastCharacterOfRunPlusOne - 1u;
270 if((bidiLineRun.characterRun.characterIndex <= characterIndex) &&
271 (characterIndex < lastCharacterOfRunPlusOne))
273 // Bidi line found. Fetch the line.
274 mBidirectionalLineIndex = bidiLineIndex;
282 BidirectionalLineRunIndex LogicalModel::GetBidirectionalLineInfo() const
284 return mBidirectionalLineIndex;
287 void LogicalModel::UpdateTextStyleRuns(CharacterIndex index, int numberOfCharacters)
289 const Length totalNumberOfCharacters = mText.Count();
291 // Process the color runs.
292 Vector<ColorRun> removedColorRuns;
293 UpdateCharacterRuns<ColorRun>(index,
295 totalNumberOfCharacters,
299 // This is needed until now for underline tag in mark-up processor
300 // Process the underlined runs.
301 Vector<UnderlinedCharacterRun> removedUnderlinedCharacterRuns;
302 UpdateCharacterRuns<UnderlinedCharacterRun>(index,
304 totalNumberOfCharacters,
305 mUnderlinedCharacterRuns,
306 removedUnderlinedCharacterRuns);
308 // Process the strikethrough runs.
309 Vector<StrikethroughCharacterRun> removedStrikethroughCharacterRuns;
310 UpdateCharacterRuns<StrikethroughCharacterRun>(index,
312 totalNumberOfCharacters,
313 mStrikethroughCharacterRuns,
314 removedStrikethroughCharacterRuns);
316 // Process the background color runs.
317 Vector<ColorRun> removedBackgroundColorRuns;
318 UpdateCharacterRuns<ColorRun>(index,
320 totalNumberOfCharacters,
321 mBackgroundColorRuns,
322 removedBackgroundColorRuns);
324 // Process the font description runs.
325 Vector<FontDescriptionRun> removedFontDescriptionRuns;
326 UpdateCharacterRuns<FontDescriptionRun>(index,
328 totalNumberOfCharacters,
329 mFontDescriptionRuns,
330 removedFontDescriptionRuns);
332 // Free memory allocated for the font family name.
333 FreeFontFamilyNames(removedFontDescriptionRuns);
336 void LogicalModel::RetrieveStyle(CharacterIndex index, InputStyle& style)
338 unsigned int runIndex = 0u;
340 // Set the text color.
341 bool colorOverriden = false;
342 unsigned int colorIndex = 0u;
343 const ColorRun* const colorRunsBuffer = mColorRuns.Begin();
344 for(Vector<ColorRun>::ConstIterator it = colorRunsBuffer,
345 endIt = mColorRuns.End();
349 const ColorRun& colorRun = *it;
351 if((colorRun.characterRun.characterIndex <= index) &&
352 (index < colorRun.characterRun.characterIndex + colorRun.characterRun.numberOfCharacters))
354 colorIndex = runIndex;
355 colorOverriden = true;
359 // Set the text's color if it's overriden.
362 style.textColor = (*(colorRunsBuffer + colorIndex)).color;
363 style.isDefaultColor = false;
366 // Reset the run index.
369 // Set the font's parameters.
370 bool nameOverriden = false;
371 bool weightOverriden = false;
372 bool widthOverriden = false;
373 bool slantOverriden = false;
374 bool sizeOverriden = false;
375 unsigned int nameIndex = 0u;
376 unsigned int weightIndex = 0u;
377 unsigned int widthIndex = 0u;
378 unsigned int slantIndex = 0u;
379 unsigned int sizeIndex = 0u;
380 const FontDescriptionRun* const fontDescriptionRunsBuffer = mFontDescriptionRuns.Begin();
381 for(Vector<FontDescriptionRun>::ConstIterator it = fontDescriptionRunsBuffer,
382 endIt = mFontDescriptionRuns.End();
386 const FontDescriptionRun& fontDescriptionRun = *it;
388 if((fontDescriptionRun.characterRun.characterIndex <= index) &&
389 (index < fontDescriptionRun.characterRun.characterIndex + fontDescriptionRun.characterRun.numberOfCharacters))
391 if(fontDescriptionRun.familyDefined)
393 nameIndex = runIndex;
394 nameOverriden = true;
397 if(fontDescriptionRun.weightDefined)
399 weightIndex = runIndex;
400 weightOverriden = true;
403 if(fontDescriptionRun.widthDefined)
405 widthIndex = runIndex;
406 widthOverriden = true;
409 if(fontDescriptionRun.slantDefined)
411 slantIndex = runIndex;
412 slantOverriden = true;
415 if(fontDescriptionRun.sizeDefined)
417 sizeIndex = runIndex;
418 sizeOverriden = true;
423 // Set the font's family name if it's overriden.
426 const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + nameIndex);
428 style.familyName = std::string(fontDescriptionRun.familyName, fontDescriptionRun.familyLength);
429 style.isFamilyDefined = true;
432 // Set the font's weight if it's overriden.
435 const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + weightIndex);
437 style.weight = fontDescriptionRun.weight;
438 style.isWeightDefined = true;
441 // Set the font's width if it's overriden.
444 const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + widthIndex);
446 style.width = fontDescriptionRun.width;
447 style.isWidthDefined = true;
450 // Set the font's slant if it's overriden.
453 const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + slantIndex);
455 style.slant = fontDescriptionRun.slant;
456 style.isSlantDefined = true;
459 // Set the font's size if it's overriden.
462 const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + sizeIndex);
464 style.size = static_cast<float>(fontDescriptionRun.size) / 64.f;
465 style.isSizeDefined = true;
469 void LogicalModel::ClearFontDescriptionRuns()
471 FreeFontFamilyNames(mFontDescriptionRuns);
474 void LogicalModel::ClearStrikethroughRuns()
476 mStrikethroughCharacterRuns.Clear();
479 void LogicalModel::CreateParagraphInfo(CharacterIndex startIndex,
480 Length numberOfCharacters)
482 const Length totalNumberOfCharacters = mLineBreakInfo.Count();
484 // Count the number of LINE_MUST_BREAK to reserve some space for the vector of paragraph's info.
485 Vector<CharacterIndex> paragraphs;
486 paragraphs.Reserve(numberOfCharacters);
487 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = mLineBreakInfo.Begin();
488 const CharacterIndex lastCharacterIndexPlusOne = startIndex + numberOfCharacters;
489 for(Length index = startIndex; index < lastCharacterIndexPlusOne; ++index)
491 if(TextAbstraction::LINE_MUST_BREAK == *(lineBreakInfoBuffer + index))
493 paragraphs.PushBack(index);
497 // Whether the current paragraphs are updated or set from scratch.
498 const bool updateCurrentParagraphs = numberOfCharacters < totalNumberOfCharacters;
500 // Reserve space for current paragraphs plus new ones.
501 const Length numberOfNewParagraphs = paragraphs.Count();
502 const Length totalNumberOfParagraphs = mParagraphInfo.Count() + numberOfNewParagraphs;
503 mParagraphInfo.Resize(totalNumberOfParagraphs);
505 ParagraphRun* paragraphInfoBuffer = NULL;
506 Vector<ParagraphRun> newParagraphs;
508 if(updateCurrentParagraphs)
510 newParagraphs.Resize(numberOfNewParagraphs);
511 paragraphInfoBuffer = newParagraphs.Begin();
515 paragraphInfoBuffer = mParagraphInfo.Begin();
518 // Find where to insert the new paragraphs.
519 ParagraphRunIndex paragraphIndex = 0u;
520 CharacterIndex firstIndex = startIndex;
522 if(updateCurrentParagraphs)
524 for(Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
525 endIt = mParagraphInfo.Begin() + totalNumberOfParagraphs - numberOfNewParagraphs;
529 const ParagraphRun& paragraph(*it);
531 if(startIndex < paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters)
533 firstIndex = paragraph.characterRun.characterIndex;
541 // Create the paragraph info.
542 ParagraphRunIndex newParagraphIndex = 0u;
543 for(Vector<CharacterIndex>::ConstIterator it = paragraphs.Begin(),
544 endIt = paragraphs.End();
546 ++it, ++newParagraphIndex)
548 const CharacterIndex index = *it;
550 ParagraphRun& paragraph = *(paragraphInfoBuffer + newParagraphIndex);
551 paragraph.characterRun.characterIndex = firstIndex;
552 paragraph.characterRun.numberOfCharacters = 1u + index - firstIndex;
554 firstIndex += paragraph.characterRun.numberOfCharacters;
557 // Insert the new paragraphs.
558 if(updateCurrentParagraphs)
560 mParagraphInfo.Insert(mParagraphInfo.Begin() + paragraphIndex,
561 newParagraphs.Begin(),
562 newParagraphs.End());
564 mParagraphInfo.Resize(totalNumberOfParagraphs);
566 // Update the next paragraph indices.
567 for(Vector<ParagraphRun>::Iterator it = mParagraphInfo.Begin() + paragraphIndex + newParagraphs.Count(),
568 endIt = mParagraphInfo.End();
572 ParagraphRun& paragraph(*it);
574 paragraph.characterRun.characterIndex += numberOfCharacters;
579 void LogicalModel::FindParagraphs(CharacterIndex index,
580 Length numberOfCharacters,
581 Vector<ParagraphRunIndex>& paragraphs)
583 // Reserve som space for the paragraph indices.
584 paragraphs.Reserve(mParagraphInfo.Count());
586 // Traverse the paragraphs to find which ones contain the given characters.
587 ParagraphRunIndex paragraphIndex = 0u;
588 for(Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
589 endIt = mParagraphInfo.End();
591 ++it, ++paragraphIndex)
593 const ParagraphRun& paragraph(*it);
595 if((paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters > index) &&
596 (paragraph.characterRun.characterIndex < index + numberOfCharacters))
598 paragraphs.PushBack(paragraphIndex);
603 void LogicalModel::ClearEmbeddedImages()
605 FreeEmbeddedItems(mEmbeddedItems);
608 void LogicalModel::ClearAnchors()
610 FreeAnchors(mAnchors);
613 LogicalModel::~LogicalModel()
615 ClearFontDescriptionRuns();
616 ClearEmbeddedImages();
619 LogicalModel::LogicalModel()
620 : mBidirectionalLineIndex(0u)
626 } // namespace Toolkit