2 * Copyright (c) 2015 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/internal/text/text-abstraction/bidirectional-support-impl.h>
22 #include <dali/devel-api/common/singleton-service.h>
23 #include <dali/integration-api/debug.h>
24 #include <fribidi/fribidi.h>
29 namespace TextAbstraction
35 typedef unsigned char BidiDirection;
37 // Internal charcter's direction.
38 const BidiDirection LEFT_TO_RIGHT = 0u;
39 const BidiDirection NEUTRAL = 1u;
40 const BidiDirection RIGHT_TO_LEFT = 2u;
43 * @param[in] paragraphDirection The FriBiDi paragraph's direction.
45 * @return Whether the paragraph is right to left.
47 bool GetBidiParagraphDirection(FriBidiParType paragraphDirection)
49 switch(paragraphDirection)
51 case FRIBIDI_PAR_RTL: // Right-To-Left paragraph.
52 case FRIBIDI_PAR_WRTL: // Weak Right To Left paragraph.
56 case FRIBIDI_PAR_LTR: // Left-To-Right paragraph.
57 case FRIBIDI_PAR_ON: // DirectiOn-Neutral paragraph.
58 case FRIBIDI_PAR_WLTR: // Weak Left To Right paragraph.
68 struct BidirectionalSupport::Plugin
71 * Stores bidirectional info per paragraph.
73 struct BidirectionalInfo
75 FriBidiCharType* characterTypes; ///< The type of each character (right, left, neutral, ...)
76 FriBidiBracketType* bracketTypes; ///< Input list of bracket types as returned by fribidi_get_bracket_types().
77 FriBidiLevel* embeddedLevels; ///< Embedded levels.
78 FriBidiParType paragraphDirection; ///< The paragraph's direction.
82 : mParagraphBidirectionalInfo(),
89 // free all resources.
90 for(Vector<BidirectionalInfo*>::Iterator it = mParagraphBidirectionalInfo.Begin(),
91 endIt = mParagraphBidirectionalInfo.End();
95 BidirectionalInfo* info = *it;
97 free(info->embeddedLevels);
98 free(info->characterTypes);
99 free(info->bracketTypes);
104 BidiInfoIndex CreateInfo(const Character* const paragraph,
105 Length numberOfCharacters,
106 bool matchLayoutDirection,
107 LayoutDirection::Type layoutDirection)
109 // Reserve memory for the paragraph's bidirectional info.
110 BidirectionalInfo* bidirectionalInfo = new BidirectionalInfo();
112 bidirectionalInfo->characterTypes = reinterpret_cast<FriBidiCharType*>(malloc(numberOfCharacters * sizeof(FriBidiCharType)));
113 if(DALI_UNLIKELY(!bidirectionalInfo->characterTypes))
115 DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", numberOfCharacters * sizeof(FriBidiCharType));
116 delete bidirectionalInfo;
120 bidirectionalInfo->embeddedLevels = reinterpret_cast<FriBidiLevel*>(malloc(numberOfCharacters * sizeof(FriBidiLevel)));
121 if(DALI_UNLIKELY(!bidirectionalInfo->embeddedLevels))
123 DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", numberOfCharacters * sizeof(FriBidiLevel));
124 free(bidirectionalInfo->characterTypes);
125 delete bidirectionalInfo;
129 // Retrieve the type of each character..
130 fribidi_get_bidi_types(paragraph, numberOfCharacters, bidirectionalInfo->characterTypes);
132 // Retrieve the paragraph's direction.
133 bidirectionalInfo->paragraphDirection = matchLayoutDirection == true ? (layoutDirection == LayoutDirection::RIGHT_TO_LEFT ? FRIBIDI_PAR_RTL : FRIBIDI_PAR_LTR) : (fribidi_get_par_direction(bidirectionalInfo->characterTypes, numberOfCharacters));
135 bidirectionalInfo->bracketTypes = reinterpret_cast<FriBidiBracketType*>(malloc(numberOfCharacters * sizeof(FriBidiBracketType)));
136 if(!bidirectionalInfo->bracketTypes)
138 DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", numberOfCharacters * sizeof(FriBidiBracketType));
139 free(bidirectionalInfo->embeddedLevels);
140 free(bidirectionalInfo->characterTypes);
141 delete bidirectionalInfo;
145 fribidi_get_bracket_types(paragraph, numberOfCharacters, bidirectionalInfo->characterTypes, bidirectionalInfo->bracketTypes);
147 // Retrieve the embedding levels.
148 if(fribidi_get_par_embedding_levels_ex(bidirectionalInfo->characterTypes, bidirectionalInfo->bracketTypes, numberOfCharacters, &bidirectionalInfo->paragraphDirection, bidirectionalInfo->embeddedLevels) == 0)
150 free(bidirectionalInfo->bracketTypes);
151 free(bidirectionalInfo->embeddedLevels);
152 free(bidirectionalInfo->characterTypes);
153 delete bidirectionalInfo;
157 // Store the bidirectional info and return the index.
158 BidiInfoIndex index = 0u;
159 if(0u != mFreeIndices.Count())
161 Vector<BidiInfoIndex>::Iterator it = mFreeIndices.End() - 1u;
165 mFreeIndices.Remove(it);
167 *(mParagraphBidirectionalInfo.Begin() + index) = bidirectionalInfo;
171 index = static_cast<BidiInfoIndex>(mParagraphBidirectionalInfo.Count());
173 mParagraphBidirectionalInfo.PushBack(bidirectionalInfo);
179 void DestroyInfo(BidiInfoIndex bidiInfoIndex)
181 if(bidiInfoIndex >= mParagraphBidirectionalInfo.Count())
186 // Retrieve the paragraph's bidirectional info.
187 Vector<BidirectionalInfo*>::Iterator it = mParagraphBidirectionalInfo.Begin() + bidiInfoIndex;
188 BidirectionalInfo* bidirectionalInfo = *it;
190 if(NULL != bidirectionalInfo)
192 // Free resources and destroy the container.
193 free(bidirectionalInfo->embeddedLevels);
194 free(bidirectionalInfo->characterTypes);
195 free(bidirectionalInfo->bracketTypes);
196 delete bidirectionalInfo;
201 // Add the index to the free indices vector.
202 mFreeIndices.PushBack(bidiInfoIndex);
205 void Reorder(BidiInfoIndex bidiInfoIndex,
206 CharacterIndex firstCharacterIndex,
207 Length numberOfCharacters,
208 CharacterIndex* visualToLogicalMap)
210 const FriBidiFlags flags = FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC;
212 // Retrieve the paragraph's bidirectional info.
213 const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
215 // Initialize the visual to logical mapping table to the identity. Otherwise fribidi_reorder_line fails to retrieve a valid mapping table.
216 for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
218 visualToLogicalMap[index] = index;
221 // Copy embedded levels as fribidi_reorder_line() may change them.
222 const size_t embeddedLevelsSize = static_cast<std::size_t>(numberOfCharacters) * sizeof(FriBidiLevel);
223 FriBidiLevel* embeddedLevels = reinterpret_cast<FriBidiLevel*>(malloc(embeddedLevelsSize));
224 if(DALI_LIKELY(embeddedLevels))
226 memcpy(embeddedLevels, bidirectionalInfo->embeddedLevels + firstCharacterIndex, embeddedLevelsSize);
229 if(fribidi_reorder_line(flags,
230 bidirectionalInfo->characterTypes + firstCharacterIndex,
233 bidirectionalInfo->paragraphDirection,
236 reinterpret_cast<FriBidiStrIndex*>(visualToLogicalMap)) == 0)
238 DALI_LOG_ERROR("fribidi_reorder_line is failed\n");
242 free(embeddedLevels);
246 DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", embeddedLevelsSize);
250 bool GetMirroredText(Character* text,
251 CharacterDirection* directions,
252 Length numberOfCharacters) const
254 bool updated = false;
256 for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
258 // Get a reference to the character inside the text.
259 Character& character = *(text + index);
261 // Retrieve the mirrored character.
262 FriBidiChar mirroredCharacter = character;
263 bool mirrored = false;
264 if(*(directions + index))
266 mirrored = fribidi_get_mirror_char(character, &mirroredCharacter);
268 updated = updated || mirrored;
270 // Update the character inside the text.
271 character = mirroredCharacter;
277 bool GetParagraphDirection(BidiInfoIndex bidiInfoIndex) const
279 // Retrieve the paragraph's bidirectional info.
280 const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
282 return GetBidiParagraphDirection(bidirectionalInfo->paragraphDirection);
285 void GetCharactersDirection(BidiInfoIndex bidiInfoIndex,
286 CharacterDirection* directions,
287 Length numberOfCharacters)
289 const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
291 for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
293 CharacterDirection& characterDirection = *(directions + index);
295 // Checks if the character is rtl oriented.
296 // I.e even a neutral character can become rtl if surrounded by rtl characters.
297 characterDirection = FRIBIDI_IS_RTL(*(bidirectionalInfo->embeddedLevels + index));
300 // We are discontinuing the previous character direction determination logic.
301 // The previous logic was too heuristic and had many shortcomings in handling various RTL cases.
302 // The key change in this update is that character direction is determined based on embedding levels,
303 // including bracket information.
304 // The character direction determined here will affect the behavior of the GetMirroredText() function.
305 // BTW, Harfbuzz(hb_shape) also supports text mirroring.
306 // To use this, we need to pass the character direction at the embedding level to hb_buffer_set_direction,
307 // which is currently challenging for us.
308 // If this is implemented, we will no longer need to perform GetMirroredText().
312 Vector<BidirectionalInfo*> mParagraphBidirectionalInfo; ///< Stores the bidirectional info per paragraph.
313 Vector<BidiInfoIndex> mFreeIndices; ///< Stores indices of free positions in the bidirectional info vector.
316 BidirectionalSupport::BidirectionalSupport()
321 BidirectionalSupport::~BidirectionalSupport()
326 TextAbstraction::BidirectionalSupport BidirectionalSupport::Get()
328 TextAbstraction::BidirectionalSupport bidirectionalSupportHandle;
330 SingletonService service(SingletonService::Get());
333 // Check whether the singleton is already created
334 BaseHandle handle = service.GetSingleton(typeid(TextAbstraction::BidirectionalSupport));
337 // If so, downcast the handle
338 BidirectionalSupport* impl = dynamic_cast<Internal::BidirectionalSupport*>(handle.GetObjectPtr());
339 bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport(impl);
341 else // create and register the object
343 bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport(new BidirectionalSupport);
344 service.Register(typeid(bidirectionalSupportHandle), bidirectionalSupportHandle);
348 return bidirectionalSupportHandle;
351 BidiInfoIndex BidirectionalSupport::CreateInfo(const Character* const paragraph,
352 Length numberOfCharacters,
353 bool matchLayoutDirection,
354 Dali::LayoutDirection::Type layoutDirection)
358 return mPlugin->CreateInfo(paragraph,
360 matchLayoutDirection,
364 void BidirectionalSupport::DestroyInfo(BidiInfoIndex bidiInfoIndex)
368 mPlugin->DestroyInfo(bidiInfoIndex);
371 void BidirectionalSupport::Reorder(BidiInfoIndex bidiInfoIndex,
372 CharacterIndex firstCharacterIndex,
373 Length numberOfCharacters,
374 CharacterIndex* visualToLogicalMap)
378 mPlugin->Reorder(bidiInfoIndex,
384 bool BidirectionalSupport::GetMirroredText(Character* text,
385 CharacterDirection* directions,
386 Length numberOfCharacters)
390 return mPlugin->GetMirroredText(text, directions, numberOfCharacters);
393 bool BidirectionalSupport::GetParagraphDirection(BidiInfoIndex bidiInfoIndex) const
400 return mPlugin->GetParagraphDirection(bidiInfoIndex);
403 void BidirectionalSupport::GetCharactersDirection(BidiInfoIndex bidiInfoIndex,
404 CharacterDirection* directions,
405 Length numberOfCharacters)
409 mPlugin->GetCharactersDirection(bidiInfoIndex,
414 void BidirectionalSupport::CreatePlugin()
418 mPlugin = new Plugin();
422 } // namespace Internal
424 } // namespace TextAbstraction