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>
36 namespace TextAbstraction
42 const char* DEFAULT_LANGUAGE = "en";
43 const unsigned int DEFAULT_LANGUAGE_LENGTH = 2u;
44 const float FROM_266 = 1.0f / 64.0f;
46 const hb_script_t SCRIPT_TO_HARFBUZZ[] =
50 HB_SCRIPT_COMMON, // ASCII_DIGITS
51 HB_SCRIPT_COMMON, // ASCII_PS
53 HB_SCRIPT_COMMON, // C1_CONTROLS
54 HB_SCRIPT_COMMON, // C1_PS
55 HB_SCRIPT_COMMON, // C1_MATH
56 HB_SCRIPT_COMMON, // SML_P
57 HB_SCRIPT_COMMON, // PHONETIC_U
58 HB_SCRIPT_COMMON, // PHONETIC_SS
59 HB_SCRIPT_COMMON, // NUMERIC_SS
60 HB_SCRIPT_COMMON, // LETTER_LIKE
61 HB_SCRIPT_COMMON, // NUMBER_FORMS
62 HB_SCRIPT_COMMON, // FRACTIONS_NF
63 HB_SCRIPT_COMMON, // NON_LATIN_LED
64 HB_SCRIPT_COMMON, // HWFW_S
103 HB_SCRIPT_MEETEI_MAYEK,
105 HB_SCRIPT_UNKNOWN, // EMOJI
106 HB_SCRIPT_UNKNOWN, // SYMBOLS1
107 HB_SCRIPT_UNKNOWN, // SYMBOLS2
108 HB_SCRIPT_UNKNOWN, // SYMBOLS3
109 HB_SCRIPT_UNKNOWN, // SYMBOLS4
110 HB_SCRIPT_UNKNOWN, // SYMBOLS5
114 struct Shaping::Plugin
117 : mFreeTypeLibrary( NULL ),
127 FT_Done_FreeType( mFreeTypeLibrary );
132 int error = FT_Init_FreeType( &mFreeTypeLibrary );
133 if( FT_Err_Ok != error )
135 DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
139 Length Shape( const Character* const text,
140 Length numberOfCharacters,
144 // Clear previoursly shaped texts.
147 mCharacterMap.Clear();
151 // Reserve some space to avoid reallocations.
152 const Length numberOfGlyphs = static_cast<Length>( 1.3f * static_cast<float>( numberOfCharacters ) );
153 mIndices.Reserve( numberOfGlyphs );
154 mAdvance.Reserve( numberOfGlyphs );
155 mCharacterMap.Reserve( numberOfGlyphs );
156 mOffset.Reserve( 2u * numberOfGlyphs );
158 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
160 // Get the font's path file name from the font Id.
161 FontDescription fontDescription;
162 fontClient.GetDescription( fontId, fontDescription );
164 // Create a FreeType font's face.
166 FT_Error retVal = FT_New_Face( mFreeTypeLibrary, fontDescription.path.c_str(), 0u, &face );
167 if( FT_Err_Ok != retVal )
169 DALI_LOG_ERROR( "Failed to open face: %s\n", fontDescription.path.c_str() );
173 unsigned int horizontalDpi = 0u;
174 unsigned int verticalDpi = 0u;
175 fontClient.GetDpi( horizontalDpi, verticalDpi );
177 FT_Set_Char_Size( face,
179 fontClient.GetPointSize( fontId ),
183 /* Get our harfbuzz font struct */
184 hb_font_t* harfBuzzFont;
185 harfBuzzFont = hb_ft_font_create( face, NULL );
187 /* Create a buffer for harfbuzz to use */
188 hb_buffer_t* harfBuzzBuffer = hb_buffer_create();
190 const bool rtlDirection = IsRightToLeftScript( script );
191 hb_buffer_set_direction( harfBuzzBuffer,
192 rtlDirection ? HB_DIRECTION_RTL : HB_DIRECTION_LTR ); /* or LTR */
194 hb_buffer_set_script( harfBuzzBuffer,
195 SCRIPT_TO_HARFBUZZ[ script ] ); /* see hb-unicode.h */
197 hb_buffer_set_language( harfBuzzBuffer,
198 hb_language_from_string( DEFAULT_LANGUAGE,
199 DEFAULT_LANGUAGE_LENGTH ) );
201 /* Layout the text */
202 hb_buffer_add_utf32( harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters );
204 hb_shape( harfBuzzFont, harfBuzzBuffer, NULL, 0u );
207 unsigned int glyphCount;
208 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( harfBuzzBuffer, &glyphCount );
209 hb_glyph_position_t *glyphPositions = hb_buffer_get_glyph_positions( harfBuzzBuffer, &glyphCount );
210 const GlyphIndex lastGlyphIndex = glyphCount - 1u;
211 for( GlyphIndex i = 0u; i < glyphCount; )
215 // If the direction is right to left, Harfbuzz retrieves the glyphs in the visual order.
216 // The glyphs are needed in the logical order to layout the text in lines.
217 // Do not change the order of the glyphs if they belong to the same cluster.
218 GlyphIndex rtlIndex = lastGlyphIndex - i;
220 unsigned int cluster = glyphInfo[rtlIndex].cluster;
221 unsigned int previousCluster = cluster;
222 Length numberOfGlyphsInCluster = 0u;
224 while( ( cluster == previousCluster ) )
226 ++numberOfGlyphsInCluster;
227 previousCluster = cluster;
233 cluster = glyphInfo[rtlIndex].cluster;
241 rtlIndex = lastGlyphIndex - ( i + ( numberOfGlyphsInCluster - 1u ) );
243 for( GlyphIndex j = 0u; j < numberOfGlyphsInCluster; ++j )
245 const GlyphIndex index = rtlIndex + j;
247 mIndices.PushBack( glyphInfo[index].codepoint );
248 mAdvance.PushBack( floor( glyphPositions[index].x_advance * FROM_266 ) );
249 mCharacterMap.PushBack( glyphInfo[index].cluster );
250 mOffset.PushBack( floor( glyphPositions[index].x_offset * FROM_266 ) );
251 mOffset.PushBack( floor( glyphPositions[index].y_offset * FROM_266 ) );
254 i += numberOfGlyphsInCluster;
258 mIndices.PushBack( glyphInfo[i].codepoint );
259 mAdvance.PushBack( floor( glyphPositions[i].x_advance * FROM_266 ) );
260 mCharacterMap.PushBack( glyphInfo[i].cluster );
261 mOffset.PushBack( floor( glyphPositions[i].x_offset * FROM_266 ) );
262 mOffset.PushBack( floor( glyphPositions[i].y_offset * FROM_266 ) );
269 hb_buffer_destroy( harfBuzzBuffer );
270 hb_font_destroy( harfBuzzFont );
271 FT_Done_Face( face );
273 return mIndices.Count();
276 void GetGlyphs( GlyphInfo* glyphInfo,
277 CharacterIndex* glyphToCharacterMap )
279 Vector<CharacterIndex>::ConstIterator indicesIt = mIndices.Begin();
280 Vector<float>::ConstIterator advanceIt = mAdvance.Begin();
281 Vector<float>::ConstIterator offsetIt = mOffset.Begin();
282 Vector<CharacterIndex>::ConstIterator characterMapIt = mCharacterMap.Begin();
284 for( GlyphIndex index = 0u, size = mIndices.Count(); index < size; ++index )
286 GlyphInfo& glyph = *( glyphInfo + index );
287 CharacterIndex& glyphToCharacter = *( glyphToCharacterMap + index );
289 glyph.fontId = mFontId;
290 glyph.index = *( indicesIt + index );
291 glyph.advance = *( advanceIt + index );
293 const GlyphIndex offsetIndex = 2u * index;
294 glyph.xBearing = *( offsetIt + offsetIndex );
295 glyph.yBearing = *( offsetIt + offsetIndex + 1u );
297 glyphToCharacter = *( characterMapIt + index );
301 FT_Library mFreeTypeLibrary;
303 Vector<CharacterIndex> mIndices;
304 Vector<float> mAdvance;
305 Vector<float> mOffset;
306 Vector<CharacterIndex> mCharacterMap;
320 TextAbstraction::Shaping Shaping::Get()
322 TextAbstraction::Shaping shapingHandle;
324 SingletonService service( SingletonService::Get() );
327 // Check whether the singleton is already created
328 Dali::BaseHandle handle = service.GetSingleton( typeid( TextAbstraction::Shaping ) );
331 // If so, downcast the handle
332 Shaping* impl = dynamic_cast< Internal::Shaping* >( handle.GetObjectPtr() );
333 shapingHandle = TextAbstraction::Shaping( impl );
335 else // create and register the object
337 shapingHandle = TextAbstraction::Shaping( new Shaping );
338 service.Register( typeid( shapingHandle ), shapingHandle );
342 return shapingHandle;
345 Length Shaping::Shape( const Character* const text,
346 Length numberOfCharacters,
352 return mPlugin->Shape( text,
358 void Shaping::GetGlyphs( GlyphInfo* glyphInfo,
359 CharacterIndex* glyphToCharacterMap )
363 mPlugin->GetGlyphs( glyphInfo,
364 glyphToCharacterMap );
367 void Shaping::CreatePlugin()
371 mPlugin = new Plugin();
372 mPlugin->Initialize();
376 } // namespace Internal
378 } // namespace TextAbstraction