2 * Copyright (c) 2019 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/shaping-impl.h>
22 #include <dali/internal/system/common/singleton-service-impl.h>
23 #include <dali/devel-api/text-abstraction/font-client.h>
24 #include <dali/devel-api/text-abstraction/glyph-info.h>
25 #include <dali/integration-api/debug.h>
26 #include "font-client-impl.h"
29 #include <harfbuzz/hb.h>
30 #include <harfbuzz/hb-ft.h>
35 #if defined(DEBUG_ENABLED)
36 Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
44 namespace TextAbstraction
50 const char* const DEFAULT_LANGUAGE = "en";
51 const unsigned int DEFAULT_LANGUAGE_LENGTH = 2u;
52 const float FROM_266 = 1.0f / 64.0f;
54 const hb_script_t SCRIPT_TO_HARFBUZZ[] =
58 HB_SCRIPT_COMMON, // ASCII_DIGITS
59 HB_SCRIPT_COMMON, // ASCII_PS
61 HB_SCRIPT_COMMON, // C1_CONTROLS
62 HB_SCRIPT_COMMON, // C1_PS
63 HB_SCRIPT_COMMON, // C1_MATH
64 HB_SCRIPT_COMMON, // SML_P
65 HB_SCRIPT_COMMON, // PHONETIC_U
66 HB_SCRIPT_COMMON, // PHONETIC_SS
67 HB_SCRIPT_COMMON, // NUMERIC_SS
68 HB_SCRIPT_COMMON, // LETTER_LIKE
69 HB_SCRIPT_COMMON, // NUMBER_FORMS
70 HB_SCRIPT_COMMON, // FRACTIONS_NF
71 HB_SCRIPT_COMMON, // NON_LATIN_LED
72 HB_SCRIPT_COMMON, // HWFW_S
111 HB_SCRIPT_MEETEI_MAYEK,
113 HB_SCRIPT_UNKNOWN, // EMOJI
114 HB_SCRIPT_UNKNOWN, // SYMBOLS1
115 HB_SCRIPT_UNKNOWN, // SYMBOLS2
116 HB_SCRIPT_UNKNOWN, // SYMBOLS3
117 HB_SCRIPT_UNKNOWN, // SYMBOLS4
118 HB_SCRIPT_UNKNOWN, // SYMBOLS5
122 struct Shaping::Plugin
136 Length Shape( const Character* const text,
137 Length numberOfCharacters,
141 // Clear previoursly shaped texts.
144 mCharacterMap.Clear();
148 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
149 TextAbstraction::Internal::FontClient& fontClientImpl = TextAbstraction::GetImplementation( fontClient );
151 const FontDescription::Type type = fontClientImpl.GetFontType( fontId );
155 case FontDescription::FACE_FONT:
157 // Reserve some space to avoid reallocations.
158 const Length numberOfGlyphs = static_cast<Length>( 1.3f * static_cast<float>( numberOfCharacters ) );
159 mIndices.Reserve( numberOfGlyphs );
160 mAdvance.Reserve( numberOfGlyphs );
161 mCharacterMap.Reserve( numberOfGlyphs );
162 mOffset.Reserve( 2u * numberOfGlyphs );
164 // Retrieve a FreeType font's face.
165 FT_Face face = fontClientImpl.GetFreetypeFace( fontId );
166 if( nullptr == face )
168 // Nothing to do if the face is null.
172 unsigned int horizontalDpi = 0u;
173 unsigned int verticalDpi = 0u;
174 fontClient.GetDpi( horizontalDpi, verticalDpi );
176 FT_Set_Char_Size( face,
178 fontClient.GetPointSize( fontId ),
182 /* Get our harfbuzz font struct */
183 hb_font_t* harfBuzzFont;
184 harfBuzzFont = hb_ft_font_create( face, NULL );
186 /* Create a buffer for harfbuzz to use */
187 hb_buffer_t* harfBuzzBuffer = hb_buffer_create();
189 const bool rtlDirection = IsRightToLeftScript( script );
190 hb_buffer_set_direction( harfBuzzBuffer,
191 rtlDirection ? HB_DIRECTION_RTL : HB_DIRECTION_LTR ); /* or LTR */
193 hb_buffer_set_script( harfBuzzBuffer,
194 SCRIPT_TO_HARFBUZZ[ script ] ); /* see hb-unicode.h */
197 char* currentLocale = setlocale(LC_MESSAGES,NULL);
199 std::istringstream stringStream( currentLocale );
200 std::string localeString;
201 std::getline(stringStream, localeString, '_');
202 hb_buffer_set_language( harfBuzzBuffer, hb_language_from_string( localeString.c_str(), localeString.size() ) );
204 /* Layout the text */
205 hb_buffer_add_utf32( harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters );
207 hb_shape( harfBuzzFont, harfBuzzBuffer, NULL, 0u );
210 unsigned int glyphCount;
211 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( harfBuzzBuffer, &glyphCount );
212 hb_glyph_position_t *glyphPositions = hb_buffer_get_glyph_positions( harfBuzzBuffer, &glyphCount );
213 const GlyphIndex lastGlyphIndex = glyphCount - 1u;
215 for( GlyphIndex i = 0u; i < glyphCount; )
219 // If the direction is right to left, Harfbuzz retrieves the glyphs in the visual order.
220 // The glyphs are needed in the logical order to layout the text in lines.
221 // Do not change the order of the glyphs if they belong to the same cluster.
222 GlyphIndex rtlIndex = lastGlyphIndex - i;
224 unsigned int cluster = glyphInfo[rtlIndex].cluster;
225 unsigned int previousCluster = cluster;
226 Length numberOfGlyphsInCluster = 0u;
228 while( ( cluster == previousCluster ) )
230 ++numberOfGlyphsInCluster;
231 previousCluster = cluster;
237 cluster = glyphInfo[rtlIndex].cluster;
245 rtlIndex = lastGlyphIndex - ( i + ( numberOfGlyphsInCluster - 1u ) );
247 for( GlyphIndex j = 0u; j < numberOfGlyphsInCluster; ++j )
249 const GlyphIndex index = rtlIndex + j;
251 mIndices.PushBack( glyphInfo[index].codepoint );
252 mAdvance.PushBack( floor( glyphPositions[index].x_advance * FROM_266 ) );
253 mCharacterMap.PushBack( glyphInfo[index].cluster );
254 mOffset.PushBack( floor( glyphPositions[index].x_offset * FROM_266 ) );
255 mOffset.PushBack( floor( glyphPositions[index].y_offset * FROM_266 ) );
258 i += numberOfGlyphsInCluster;
262 mIndices.PushBack( glyphInfo[i].codepoint );
263 mAdvance.PushBack( floor( glyphPositions[i].x_advance * FROM_266 ) );
264 mCharacterMap.PushBack( glyphInfo[i].cluster );
265 mOffset.PushBack( floor( glyphPositions[i].x_offset * FROM_266 ) );
266 mOffset.PushBack( floor( glyphPositions[i].y_offset * FROM_266 ) );
273 hb_buffer_destroy( harfBuzzBuffer );
274 hb_font_destroy( harfBuzzFont );
277 case FontDescription::BITMAP_FONT:
279 // Reserve some space to avoid reallocations.
280 // The advance and offset tables can be initialized with zeros as it's not needed to get metrics from the bitmaps here.
281 mIndices.Resize( numberOfCharacters );
282 mAdvance.Resize( numberOfCharacters, 0u );
283 mCharacterMap.Reserve( numberOfCharacters );
284 mOffset.Resize( 2u * numberOfCharacters, 0.f );
286 // The utf32 character can be used as the glyph's index.
287 std::copy( text, text + numberOfCharacters, mIndices.Begin() );
289 // The glyph to character map is 1 to 1.
290 for( unsigned int index = 0u; index < numberOfCharacters; ++index )
292 mCharacterMap.PushBack( index );
298 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
302 return mIndices.Count();
305 void GetGlyphs( GlyphInfo* glyphInfo,
306 CharacterIndex* glyphToCharacterMap )
308 Vector<CharacterIndex>::ConstIterator indicesIt = mIndices.Begin();
309 Vector<float>::ConstIterator advanceIt = mAdvance.Begin();
310 Vector<float>::ConstIterator offsetIt = mOffset.Begin();
311 Vector<CharacterIndex>::ConstIterator characterMapIt = mCharacterMap.Begin();
313 for( GlyphIndex index = 0u, size = mIndices.Count(); index < size; ++index )
315 GlyphInfo& glyph = *( glyphInfo + index );
316 CharacterIndex& glyphToCharacter = *( glyphToCharacterMap + index );
318 glyph.fontId = mFontId;
319 glyph.index = *( indicesIt + index );
320 glyph.advance = *( advanceIt + index );
322 const GlyphIndex offsetIndex = 2u * index;
323 glyph.xBearing = *( offsetIt + offsetIndex );
324 glyph.yBearing = *( offsetIt + offsetIndex + 1u );
326 glyphToCharacter = *( characterMapIt + index );
330 Vector<CharacterIndex> mIndices;
331 Vector<float> mAdvance;
332 Vector<float> mOffset;
333 Vector<CharacterIndex> mCharacterMap;
347 TextAbstraction::Shaping Shaping::Get()
349 TextAbstraction::Shaping shapingHandle;
351 SingletonService service( SingletonService::Get() );
354 // Check whether the singleton is already created
355 Dali::BaseHandle handle = service.GetSingleton( typeid( TextAbstraction::Shaping ) );
358 // If so, downcast the handle
359 Shaping* impl = dynamic_cast< Internal::Shaping* >( handle.GetObjectPtr() );
360 shapingHandle = TextAbstraction::Shaping( impl );
362 else // create and register the object
364 shapingHandle = TextAbstraction::Shaping( new Shaping );
365 service.Register( typeid( shapingHandle ), shapingHandle );
369 return shapingHandle;
372 Length Shaping::Shape( const Character* const text,
373 Length numberOfCharacters,
379 return mPlugin->Shape( text,
385 void Shaping::GetGlyphs( GlyphInfo* glyphInfo,
386 CharacterIndex* glyphToCharacterMap )
390 mPlugin->GetGlyphs( glyphInfo,
391 glyphToCharacterMap );
394 void Shaping::CreatePlugin()
398 mPlugin = new Plugin();
402 } // namespace Internal
404 } // namespace TextAbstraction