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 "shaping-impl.h"
22 #include <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>
28 #include <harfbuzz/hb.h>
29 #include <harfbuzz/hb-ft.h>
37 const static uint8_t U2 = 2u;
38 const static uint8_t U3 = 3u;
39 const static uint8_t U4 = 4u;
41 uint32_t GetNumberOfUtf8Bytes( const uint32_t* const utf32, uint32_t numberOfCharacters )
43 uint32_t numberOfBytes = 0u;
45 const uint32_t* begin = utf32;
46 const uint32_t* end = utf32 + numberOfCharacters;
48 for( ; begin < end; ++begin )
50 const uint32_t code = *begin;
56 else if( code < 0x800u )
60 else if( code < 0x10000u )
64 else if( code < 0x200000u )
73 uint32_t Utf32ToUtf8( const uint32_t* const utf32, uint32_t numberOfCharacters, uint8_t* utf8 )
75 const uint32_t* begin = utf32;
76 const uint32_t* end = utf32 + numberOfCharacters;
78 uint8_t* utf8Begin = utf8;
80 for( ; begin < end; ++begin )
82 const uint32_t code = *begin;
88 else if( code < 0x800u )
90 *utf8++ = static_cast<uint8_t>( code >> 6u ) | 0xc0u; // lead byte for 2 byte sequence
91 *utf8++ = static_cast<uint8_t>( code & 0x3f ) | 0x80u; // continuation byte
93 else if( code < 0x10000u )
95 *utf8++ = static_cast<uint8_t>( code >> 12u ) | 0xe0u; // lead byte for 2 byte sequence
96 *utf8++ = static_cast<uint8_t>( ( code >> 6u ) & 0x3f ) | 0x80u; // continuation byte
97 *utf8++ = static_cast<uint8_t>( code & 0x3f ) | 0x80u; // continuation byte
99 else if( code < 0x200000u )
101 *utf8++ = static_cast<uint8_t>( code >> 18u ) | 0xf0u; // lead byte for 2 byte sequence
102 *utf8++ = static_cast<uint8_t>( ( code >> 12u ) & 0x3f ) | 0x80u; // continuation byte
103 *utf8++ = static_cast<uint8_t>( ( code >> 6u ) & 0x3f ) | 0x80u; // continuation byte
104 *utf8++ = static_cast<uint8_t>( code & 0x3f ) | 0x80u; // continuation byte
108 return utf8 - utf8Begin;
111 void Utf32ToUtf8( const uint32_t* const utf32, uint32_t numberOfCharacters, std::string& utf8 )
115 uint32_t numberOfBytes = GetNumberOfUtf8Bytes( &utf32[0], numberOfCharacters );
116 utf8.resize( numberOfBytes );
118 // This is a bit horrible but std::string returns a (signed) char*
119 Utf32ToUtf8( utf32, numberOfCharacters, reinterpret_cast<uint8_t*>(&utf8[0]) );
127 namespace TextAbstraction
133 const char* DEFAULT_LANGUAGE = "en";
134 const unsigned int DEFAULT_LANGUAGE_LENGTH = 2u;
135 const float FROM_266 = 1.0f / 64.0f;
137 const hb_script_t SCRIPT_TO_HARFBUZZ[] =
141 HB_SCRIPT_COMMON, // ASCII_DIGITS
142 HB_SCRIPT_COMMON, // ASCII_PS
144 HB_SCRIPT_COMMON, // C1_CONTROLS
145 HB_SCRIPT_COMMON, // C1_PS
146 HB_SCRIPT_COMMON, // C1_MATH
147 HB_SCRIPT_COMMON, // SML_P
148 HB_SCRIPT_COMMON, // PHONETIC_U
149 HB_SCRIPT_COMMON, // PHONETIC_SS
150 HB_SCRIPT_COMMON, // NUMERIC_SS
151 HB_SCRIPT_COMMON, // LETTER_LIKE
152 HB_SCRIPT_COMMON, // NUMBER_FORMS
153 HB_SCRIPT_COMMON, // FRACTIONS_NF
154 HB_SCRIPT_COMMON, // NON_LATIN_LED
155 HB_SCRIPT_COMMON, // HWFW_S
175 HB_SCRIPT_DEVANAGARI,
194 HB_SCRIPT_MEETEI_MAYEK,
196 HB_SCRIPT_UNKNOWN, // EMOJI
197 HB_SCRIPT_UNKNOWN, // SYMBOLS1
198 HB_SCRIPT_UNKNOWN, // SYMBOLS2
199 HB_SCRIPT_UNKNOWN, // SYMBOLS3
200 HB_SCRIPT_UNKNOWN, // SYMBOLS4
201 HB_SCRIPT_UNKNOWN, // SYMBOLS5
205 struct Shaping::Plugin
208 : mFreeTypeLibrary( NULL ),
218 FT_Done_FreeType( mFreeTypeLibrary );
223 int error = FT_Init_FreeType( &mFreeTypeLibrary );
224 if( FT_Err_Ok != error )
226 DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
230 Length Shape( const Character* const text,
231 Length numberOfCharacters,
235 // Clear previoursly shaped texts.
238 mCharacterMap.Clear();
242 // Reserve some space to avoid reallocations.
243 const Length numberOfGlyphs = static_cast<Length>( 1.3f * static_cast<float>( numberOfCharacters ) );
244 mIndices.Reserve( numberOfGlyphs );
245 mAdvance.Reserve( numberOfGlyphs );
246 mCharacterMap.Reserve( numberOfGlyphs );
247 mOffset.Reserve( 2u * numberOfGlyphs );
249 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
251 // Get the font's path file name from the font Id.
252 FontDescription fontDescription;
253 fontClient.GetDescription( fontId, fontDescription );
255 // Create a FreeType font's face.
257 FT_Error retVal = FT_New_Face( mFreeTypeLibrary, fontDescription.path.c_str(), 0u, &face );
258 if( FT_Err_Ok != retVal )
260 DALI_LOG_ERROR( "Failed to open face: %s\n", fontDescription.path.c_str() );
264 unsigned int horizontalDpi = 0u;
265 unsigned int verticalDpi = 0u;
266 fontClient.GetDpi( horizontalDpi, verticalDpi );
268 FT_Set_Char_Size( face,
270 fontClient.GetPointSize( fontId ),
274 /* Get our harfbuzz font struct */
275 hb_font_t* harfBuzzFont;
276 harfBuzzFont = hb_ft_font_create( face, NULL );
278 /* Create a buffer for harfbuzz to use */
279 hb_buffer_t* harfBuzzBuffer = hb_buffer_create();
281 const bool rtlDirection = IsRightToLeftScript( script );
282 hb_buffer_set_direction( harfBuzzBuffer,
283 rtlDirection ? HB_DIRECTION_RTL : HB_DIRECTION_LTR ); /* or LTR */
285 hb_buffer_set_script( harfBuzzBuffer,
286 SCRIPT_TO_HARFBUZZ[ script ] ); /* see hb-unicode.h */
289 char* currentLocale = setlocale(LC_MESSAGES,NULL);
291 std::istringstream stringStream( currentLocale );
292 std::string localeString;
293 std::getline(stringStream, localeString, '_');
294 hb_buffer_set_language( harfBuzzBuffer, hb_language_from_string( localeString.c_str(), localeString.size() ) );
296 /* Layout the text */
297 hb_buffer_add_utf32( harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters );
299 hb_shape( harfBuzzFont, harfBuzzBuffer, NULL, 0u );
301 std::string currentText;
302 Utf32ToUtf8( text, numberOfCharacters, currentText );
304 DALI_LOG_RELEASE_INFO( "Shape: currentText: %s, font: %s, pointSize: %d\n", currentText.c_str(), fontDescription.path.c_str(), static_cast<int>( fontClient.GetPointSize( fontId ) * FROM_266 ) );
307 unsigned int glyphCount;
308 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( harfBuzzBuffer, &glyphCount );
309 hb_glyph_position_t *glyphPositions = hb_buffer_get_glyph_positions( harfBuzzBuffer, &glyphCount );
310 const GlyphIndex lastGlyphIndex = glyphCount - 1u;
311 for( GlyphIndex i = 0u; i < glyphCount; )
315 // If the direction is right to left, Harfbuzz retrieves the glyphs in the visual order.
316 // The glyphs are needed in the logical order to layout the text in lines.
317 // Do not change the order of the glyphs if they belong to the same cluster.
318 GlyphIndex rtlIndex = lastGlyphIndex - i;
320 unsigned int cluster = glyphInfo[rtlIndex].cluster;
321 unsigned int previousCluster = cluster;
322 Length numberOfGlyphsInCluster = 0u;
324 while( ( cluster == previousCluster ) )
326 ++numberOfGlyphsInCluster;
327 previousCluster = cluster;
333 cluster = glyphInfo[rtlIndex].cluster;
341 rtlIndex = lastGlyphIndex - ( i + ( numberOfGlyphsInCluster - 1u ) );
343 for( GlyphIndex j = 0u; j < numberOfGlyphsInCluster; ++j )
345 const GlyphIndex index = rtlIndex + j;
347 mIndices.PushBack( glyphInfo[index].codepoint );
348 mAdvance.PushBack( floor( glyphPositions[index].x_advance * FROM_266 ) );
349 mCharacterMap.PushBack( glyphInfo[index].cluster );
350 mOffset.PushBack( floor( glyphPositions[index].x_offset * FROM_266 ) );
351 mOffset.PushBack( floor( glyphPositions[index].y_offset * FROM_266 ) );
354 i += numberOfGlyphsInCluster;
358 mIndices.PushBack( glyphInfo[i].codepoint );
359 mAdvance.PushBack( floor( glyphPositions[i].x_advance * FROM_266 ) );
360 mCharacterMap.PushBack( glyphInfo[i].cluster );
361 mOffset.PushBack( floor( glyphPositions[i].x_offset * FROM_266 ) );
362 mOffset.PushBack( floor( glyphPositions[i].y_offset * FROM_266 ) );
364 DALI_LOG_RELEASE_INFO( "glyphIndex: %u, glyph.advance: %f, glyph.xBearing: %f\n", glyphInfo[i].codepoint, floor( glyphPositions[i].x_advance * FROM_266 ), floor( glyphPositions[i].x_offset * FROM_266 ) );
371 hb_buffer_destroy( harfBuzzBuffer );
372 hb_font_destroy( harfBuzzFont );
373 FT_Done_Face( face );
375 return mIndices.Count();
378 void GetGlyphs( GlyphInfo* glyphInfo,
379 CharacterIndex* glyphToCharacterMap )
381 Vector<CharacterIndex>::ConstIterator indicesIt = mIndices.Begin();
382 Vector<float>::ConstIterator advanceIt = mAdvance.Begin();
383 Vector<float>::ConstIterator offsetIt = mOffset.Begin();
384 Vector<CharacterIndex>::ConstIterator characterMapIt = mCharacterMap.Begin();
386 for( GlyphIndex index = 0u, size = mIndices.Count(); index < size; ++index )
388 GlyphInfo& glyph = *( glyphInfo + index );
389 CharacterIndex& glyphToCharacter = *( glyphToCharacterMap + index );
391 glyph.fontId = mFontId;
392 glyph.index = *( indicesIt + index );
393 glyph.advance = *( advanceIt + index );
395 const GlyphIndex offsetIndex = 2u * index;
396 glyph.xBearing = *( offsetIt + offsetIndex );
397 glyph.yBearing = *( offsetIt + offsetIndex + 1u );
399 glyphToCharacter = *( characterMapIt + index );
403 FT_Library mFreeTypeLibrary;
405 Vector<CharacterIndex> mIndices;
406 Vector<float> mAdvance;
407 Vector<float> mOffset;
408 Vector<CharacterIndex> mCharacterMap;
422 TextAbstraction::Shaping Shaping::Get()
424 TextAbstraction::Shaping shapingHandle;
426 SingletonService service( SingletonService::Get() );
429 // Check whether the singleton is already created
430 Dali::BaseHandle handle = service.GetSingleton( typeid( TextAbstraction::Shaping ) );
433 // If so, downcast the handle
434 Shaping* impl = dynamic_cast< Internal::Shaping* >( handle.GetObjectPtr() );
435 shapingHandle = TextAbstraction::Shaping( impl );
437 else // create and register the object
439 shapingHandle = TextAbstraction::Shaping( new Shaping );
440 service.Register( typeid( shapingHandle ), shapingHandle );
444 return shapingHandle;
447 Length Shaping::Shape( const Character* const text,
448 Length numberOfCharacters,
454 return mPlugin->Shape( text,
460 void Shaping::GetGlyphs( GlyphInfo* glyphInfo,
461 CharacterIndex* glyphToCharacterMap )
465 mPlugin->GetGlyphs( glyphInfo,
466 glyphToCharacterMap );
469 void Shaping::CreatePlugin()
473 mPlugin = new Plugin();
474 mPlugin->Initialize();
478 } // namespace Internal
480 } // namespace TextAbstraction