fix issue when strikethrough used without ending tag
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / logical-model-impl.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/logical-model-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali-toolkit/internal/text/input-style.h>
23 #include <dali-toolkit/internal/text/text-run-container.h>
24
25 namespace Dali
26 {
27 namespace Toolkit
28 {
29 namespace Text
30 {
31 void FreeFontFamilyNames(Vector<FontDescriptionRun>& fontDescriptionRuns)
32 {
33   for(Vector<FontDescriptionRun>::Iterator it    = fontDescriptionRuns.Begin(),
34                                            endIt = fontDescriptionRuns.End();
35       it != endIt;
36       ++it)
37   {
38     delete[](*it).familyName;
39   }
40
41   fontDescriptionRuns.Clear();
42 }
43
44 void FreeEmbeddedItems(Vector<EmbeddedItem>& embeddedItem)
45 {
46   for(Vector<EmbeddedItem>::Iterator it    = embeddedItem.Begin(),
47                                      endIt = embeddedItem.End();
48       it != endIt;
49       ++it)
50   {
51     EmbeddedItem& item = *it;
52     delete[] item.url;
53   }
54
55   embeddedItem.Clear();
56 }
57
58 void FreeAnchors(Vector<Anchor>& anchors)
59 {
60   for(auto&& anchor : anchors)
61   {
62     delete[] anchor.href;
63   }
64
65   anchors.Clear();
66 }
67
68 LogicalModelPtr LogicalModel::New()
69 {
70   return LogicalModelPtr(new LogicalModel());
71 }
72
73 Script LogicalModel::GetScript(CharacterIndex characterIndex) const
74 {
75   // If this operation is too slow, consider a binary search.
76
77   const ScriptRun* const scriptRunBuffer = mScriptRuns.Begin();
78   for(Length index = 0u, length = mScriptRuns.Count(); index < length; ++index)
79   {
80     const ScriptRun* const scriptRun = scriptRunBuffer + index;
81
82     if((scriptRun->characterRun.characterIndex <= characterIndex) &&
83        (characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters))
84     {
85       return scriptRun->script;
86     }
87   }
88
89   return TextAbstraction::UNKNOWN;
90 }
91
92 CharacterDirection LogicalModel::GetCharacterDirection(CharacterIndex characterIndex) const
93 {
94   if(characterIndex >= mCharacterDirections.Count())
95   {
96     // The model has no right to left characters, so the vector of directions is void.
97     return false;
98   }
99
100   return *(mCharacterDirections.Begin() + characterIndex);
101 }
102
103 CharacterIndex LogicalModel::GetLogicalCursorIndex(CharacterIndex visualCursorIndex)
104 {
105   // The character's directions buffer.
106   const CharacterDirection* const modelCharacterDirections = mCharacterDirections.Begin();
107
108   // The bidirectional line info.
109   const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
110
111   // Whether the paragraph starts with a right to left character.
112   const bool isRightToLeftParagraph = bidirectionalLineInfo->direction;
113
114   // The total number of characters of the line.
115   const Length lastCharacterIndex = bidirectionalLineInfo->characterRun.characterIndex + bidirectionalLineInfo->characterRun.numberOfCharacters;
116
117   CharacterIndex logicalCursorIndex = 0u;
118
119   if(bidirectionalLineInfo->characterRun.characterIndex == visualCursorIndex)
120   {
121     if(isRightToLeftParagraph)
122     {
123       logicalCursorIndex = lastCharacterIndex;
124     }
125     else // else logical position is the first of the line.
126     {
127       logicalCursorIndex = bidirectionalLineInfo->characterRun.characterIndex;
128     }
129   }
130   else if(lastCharacterIndex == visualCursorIndex)
131   {
132     if(isRightToLeftParagraph)
133     {
134       logicalCursorIndex = bidirectionalLineInfo->characterRun.characterIndex;
135     }
136     else // else logical position is the number of characters.
137     {
138       logicalCursorIndex = lastCharacterIndex;
139     }
140   }
141   else
142   {
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.
146
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;
150
151     const CharacterDirection previousCharacterDirection = *(modelCharacterDirections + previousLogicalCursorIndex);
152     const CharacterDirection currentCharacterDirection  = *(modelCharacterDirections + currentLogicalCursorIndex);
153
154     if(previousCharacterDirection == currentCharacterDirection)
155     {
156       // Both glyphs have the same direction.
157       if(previousCharacterDirection)
158       {
159         logicalCursorIndex = previousLogicalCursorIndex;
160       }
161       else
162       {
163         logicalCursorIndex = currentLogicalCursorIndex;
164       }
165     }
166     else
167     {
168       if(isRightToLeftParagraph)
169       {
170         if(currentCharacterDirection)
171         {
172           logicalCursorIndex = currentLogicalCursorIndex + 1u;
173         }
174         else
175         {
176           logicalCursorIndex = previousLogicalCursorIndex;
177         }
178       }
179       else
180       {
181         if(previousCharacterDirection)
182         {
183           logicalCursorIndex = currentLogicalCursorIndex;
184         }
185         else
186         {
187           logicalCursorIndex = previousLogicalCursorIndex + 1u;
188         }
189       }
190     }
191   }
192
193   return logicalCursorIndex;
194 }
195
196 CharacterIndex LogicalModel::GetLogicalCharacterIndex(CharacterIndex visualCharacterIndex)
197 {
198   // The bidirectional line info.
199   const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
200
201   return *(bidirectionalLineInfo->visualToLogicalMap + visualCharacterIndex - bidirectionalLineInfo->characterRun.characterIndex) + bidirectionalLineInfo->characterRun.characterIndex;
202 }
203
204 bool LogicalModel::FetchBidirectionalLineInfo(CharacterIndex characterIndex)
205 {
206   // The number of bidirectional lines.
207   const Length numberOfBidirectionalLines = mBidirectionalLineInfo.Count();
208
209   if(0u == numberOfBidirectionalLines)
210   {
211     // If there is no bidirectional info.
212     return false;
213   }
214
215   // Find the bidi line where the character is laid-out.
216
217   const BidirectionalLineInfoRun* const bidirectionalLineInfoBuffer = mBidirectionalLineInfo.Begin();
218
219   // Check first if the character is in the previously fetched line.
220
221   BidirectionalLineRunIndex bidiLineIndex                 = 0u;
222   CharacterIndex            lastCharacterOfRightToLeftRun = 0u;
223   if(mBidirectionalLineIndex < numberOfBidirectionalLines)
224   {
225     const BidirectionalLineInfoRun& bidiLineRun = *(bidirectionalLineInfoBuffer + mBidirectionalLineIndex);
226
227     const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
228     if((bidiLineRun.characterRun.characterIndex <= characterIndex) &&
229        (characterIndex < lastCharacterOfRunPlusOne))
230     {
231       // The character is in the previously fetched bidi line.
232       return true;
233     }
234     else
235     {
236       // The character is not in the previously fetched line.
237       // Set the bidi line index from where to start the fetch.
238
239       if(characterIndex < bidiLineRun.characterRun.characterIndex)
240       {
241         // Start the fetch from the beginning.
242         bidiLineIndex = 0u;
243       }
244       else
245       {
246         // Start the fetch from the next line.
247         bidiLineIndex                 = mBidirectionalLineIndex + 1u;
248         lastCharacterOfRightToLeftRun = lastCharacterOfRunPlusOne - 1u;
249       }
250     }
251   }
252
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();
256       it != endIt;
257       ++it, ++bidiLineIndex)
258   {
259     const BidirectionalLineInfoRun& bidiLineRun = *it;
260
261     if((lastCharacterOfRightToLeftRun < characterIndex) &&
262        (characterIndex < bidiLineRun.characterRun.characterIndex))
263     {
264       // The character is not inside a bidi line.
265       return false;
266     }
267
268     const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
269     lastCharacterOfRightToLeftRun                  = lastCharacterOfRunPlusOne - 1u;
270     if((bidiLineRun.characterRun.characterIndex <= characterIndex) &&
271        (characterIndex < lastCharacterOfRunPlusOne))
272     {
273       // Bidi line found. Fetch the line.
274       mBidirectionalLineIndex = bidiLineIndex;
275       return true;
276     }
277   }
278
279   return false;
280 }
281
282 BidirectionalLineRunIndex LogicalModel::GetBidirectionalLineInfo() const
283 {
284   return mBidirectionalLineIndex;
285 }
286
287 void LogicalModel::UpdateTextStyleRuns(CharacterIndex index, int numberOfCharacters)
288 {
289   const Length totalNumberOfCharacters = mText.Count();
290
291   // Process the color runs.
292   Vector<ColorRun> removedColorRuns;
293   UpdateCharacterRuns<ColorRun>(index,
294                                 numberOfCharacters,
295                                 totalNumberOfCharacters,
296                                 mColorRuns,
297                                 removedColorRuns);
298
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,
303                                               numberOfCharacters,
304                                               totalNumberOfCharacters,
305                                               mUnderlinedCharacterRuns,
306                                               removedUnderlinedCharacterRuns);
307
308   // Process the strikethrough runs.
309   Vector<StrikethroughCharacterRun> removedStrikethroughCharacterRuns;
310   UpdateCharacterRuns<StrikethroughCharacterRun>(index,
311                                                  numberOfCharacters,
312                                                  totalNumberOfCharacters,
313                                                  mStrikethroughCharacterRuns,
314                                                  removedStrikethroughCharacterRuns);
315
316   // Process the background color runs.
317   Vector<ColorRun> removedBackgroundColorRuns;
318   UpdateCharacterRuns<ColorRun>(index,
319                                 numberOfCharacters,
320                                 totalNumberOfCharacters,
321                                 mBackgroundColorRuns,
322                                 removedBackgroundColorRuns);
323
324   // Process the font description runs.
325   Vector<FontDescriptionRun> removedFontDescriptionRuns;
326   UpdateCharacterRuns<FontDescriptionRun>(index,
327                                           numberOfCharacters,
328                                           totalNumberOfCharacters,
329                                           mFontDescriptionRuns,
330                                           removedFontDescriptionRuns);
331
332   // Free memory allocated for the font family name.
333   FreeFontFamilyNames(removedFontDescriptionRuns);
334 }
335
336 void LogicalModel::RetrieveStyle(CharacterIndex index, InputStyle& style)
337 {
338   unsigned int runIndex = 0u;
339
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();
346       it != endIt;
347       ++it, ++runIndex)
348   {
349     const ColorRun& colorRun = *it;
350
351     if((colorRun.characterRun.characterIndex <= index) &&
352        (index < colorRun.characterRun.characterIndex + colorRun.characterRun.numberOfCharacters))
353     {
354       colorIndex     = runIndex;
355       colorOverriden = true;
356     }
357   }
358
359   // Set the text's color if it's overriden.
360   if(colorOverriden)
361   {
362     style.textColor      = (*(colorRunsBuffer + colorIndex)).color;
363     style.isDefaultColor = false;
364   }
365
366   // Reset the run index.
367   runIndex = 0u;
368
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();
383       it != endIt;
384       ++it, ++runIndex)
385   {
386     const FontDescriptionRun& fontDescriptionRun = *it;
387
388     if((fontDescriptionRun.characterRun.characterIndex <= index) &&
389        (index < fontDescriptionRun.characterRun.characterIndex + fontDescriptionRun.characterRun.numberOfCharacters))
390     {
391       if(fontDescriptionRun.familyDefined)
392       {
393         nameIndex     = runIndex;
394         nameOverriden = true;
395       }
396
397       if(fontDescriptionRun.weightDefined)
398       {
399         weightIndex     = runIndex;
400         weightOverriden = true;
401       }
402
403       if(fontDescriptionRun.widthDefined)
404       {
405         widthIndex     = runIndex;
406         widthOverriden = true;
407       }
408
409       if(fontDescriptionRun.slantDefined)
410       {
411         slantIndex     = runIndex;
412         slantOverriden = true;
413       }
414
415       if(fontDescriptionRun.sizeDefined)
416       {
417         sizeIndex     = runIndex;
418         sizeOverriden = true;
419       }
420     }
421   }
422
423   // Set the font's family name if it's overriden.
424   if(nameOverriden)
425   {
426     const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + nameIndex);
427
428     style.familyName      = std::string(fontDescriptionRun.familyName, fontDescriptionRun.familyLength);
429     style.isFamilyDefined = true;
430   }
431
432   // Set the font's weight if it's overriden.
433   if(weightOverriden)
434   {
435     const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + weightIndex);
436
437     style.weight          = fontDescriptionRun.weight;
438     style.isWeightDefined = true;
439   }
440
441   // Set the font's width if it's overriden.
442   if(widthOverriden)
443   {
444     const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + widthIndex);
445
446     style.width          = fontDescriptionRun.width;
447     style.isWidthDefined = true;
448   }
449
450   // Set the font's slant if it's overriden.
451   if(slantOverriden)
452   {
453     const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + slantIndex);
454
455     style.slant          = fontDescriptionRun.slant;
456     style.isSlantDefined = true;
457   }
458
459   // Set the font's size if it's overriden.
460   if(sizeOverriden)
461   {
462     const FontDescriptionRun& fontDescriptionRun = *(fontDescriptionRunsBuffer + sizeIndex);
463
464     style.size          = static_cast<float>(fontDescriptionRun.size) / 64.f;
465     style.isSizeDefined = true;
466   }
467 }
468
469 void LogicalModel::ClearFontDescriptionRuns()
470 {
471   FreeFontFamilyNames(mFontDescriptionRuns);
472 }
473
474 void LogicalModel::ClearStrikethroughRuns()
475 {
476   mStrikethroughCharacterRuns.Clear();
477 }
478
479 void LogicalModel::CreateParagraphInfo(CharacterIndex startIndex,
480                                        Length         numberOfCharacters)
481 {
482   const Length totalNumberOfCharacters = mLineBreakInfo.Count();
483
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)
490   {
491     if(TextAbstraction::LINE_MUST_BREAK == *(lineBreakInfoBuffer + index))
492     {
493       paragraphs.PushBack(index);
494     }
495   }
496
497   // Whether the current paragraphs are updated or set from scratch.
498   const bool updateCurrentParagraphs = numberOfCharacters < totalNumberOfCharacters;
499
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);
504
505   ParagraphRun*        paragraphInfoBuffer = NULL;
506   Vector<ParagraphRun> newParagraphs;
507
508   if(updateCurrentParagraphs)
509   {
510     newParagraphs.Resize(numberOfNewParagraphs);
511     paragraphInfoBuffer = newParagraphs.Begin();
512   }
513   else
514   {
515     paragraphInfoBuffer = mParagraphInfo.Begin();
516   }
517
518   // Find where to insert the new paragraphs.
519   ParagraphRunIndex paragraphIndex = 0u;
520   CharacterIndex    firstIndex     = startIndex;
521
522   if(updateCurrentParagraphs)
523   {
524     for(Vector<ParagraphRun>::ConstIterator it    = mParagraphInfo.Begin(),
525                                             endIt = mParagraphInfo.Begin() + totalNumberOfParagraphs - numberOfNewParagraphs;
526         it != endIt;
527         ++it)
528     {
529       const ParagraphRun& paragraph(*it);
530
531       if(startIndex < paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters)
532       {
533         firstIndex = paragraph.characterRun.characterIndex;
534         break;
535       }
536
537       ++paragraphIndex;
538     }
539   }
540
541   // Create the paragraph info.
542   ParagraphRunIndex newParagraphIndex = 0u;
543   for(Vector<CharacterIndex>::ConstIterator it    = paragraphs.Begin(),
544                                             endIt = paragraphs.End();
545       it != endIt;
546       ++it, ++newParagraphIndex)
547   {
548     const CharacterIndex index = *it;
549
550     ParagraphRun& paragraph                   = *(paragraphInfoBuffer + newParagraphIndex);
551     paragraph.characterRun.characterIndex     = firstIndex;
552     paragraph.characterRun.numberOfCharacters = 1u + index - firstIndex;
553
554     firstIndex += paragraph.characterRun.numberOfCharacters;
555   }
556
557   // Insert the new paragraphs.
558   if(updateCurrentParagraphs)
559   {
560     mParagraphInfo.Insert(mParagraphInfo.Begin() + paragraphIndex,
561                           newParagraphs.Begin(),
562                           newParagraphs.End());
563
564     mParagraphInfo.Resize(totalNumberOfParagraphs);
565
566     // Update the next paragraph indices.
567     for(Vector<ParagraphRun>::Iterator it    = mParagraphInfo.Begin() + paragraphIndex + newParagraphs.Count(),
568                                        endIt = mParagraphInfo.End();
569         it != endIt;
570         ++it)
571     {
572       ParagraphRun& paragraph(*it);
573
574       paragraph.characterRun.characterIndex += numberOfCharacters;
575     }
576   }
577 }
578
579 void LogicalModel::FindParagraphs(CharacterIndex             index,
580                                   Length                     numberOfCharacters,
581                                   Vector<ParagraphRunIndex>& paragraphs)
582 {
583   // Reserve som space for the paragraph indices.
584   paragraphs.Reserve(mParagraphInfo.Count());
585
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();
590       it != endIt;
591       ++it, ++paragraphIndex)
592   {
593     const ParagraphRun& paragraph(*it);
594
595     if((paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters > index) &&
596        (paragraph.characterRun.characterIndex < index + numberOfCharacters))
597     {
598       paragraphs.PushBack(paragraphIndex);
599     }
600   }
601 }
602
603 void LogicalModel::ClearEmbeddedImages()
604 {
605   FreeEmbeddedItems(mEmbeddedItems);
606 }
607
608 void LogicalModel::ClearAnchors()
609 {
610   FreeAnchors(mAnchors);
611 }
612
613 LogicalModel::~LogicalModel()
614 {
615   ClearFontDescriptionRuns();
616   ClearEmbeddedImages();
617 }
618
619 LogicalModel::LogicalModel()
620 : mBidirectionalLineIndex(0u)
621 {
622 }
623
624 } // namespace Text
625
626 } // namespace Toolkit
627
628 } // namespace Dali