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/public-api/text-abstraction/font-client.h>
24 #include <dali/public-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 unsigned int HIGH_QUALITY_PIXEL_SIZE = 200u; // Pixel size sent to FreeType2 FT_Set_Char_Size() for high quality glyphs.
43 const char* DEFAULT_LANGUAGE = "en";
44 const unsigned int DEFAULT_LANGUAGE_LENGTH = 2u;
45 const float TO_PIXELS = 64.f;
47 const hb_script_t SCRIPT_TO_HARFBUZZ[] =
70 struct Shaping::Plugin
73 : mFreeTypeLibrary( NULL ),
83 FT_Done_FreeType( mFreeTypeLibrary );
88 int error = FT_Init_FreeType( &mFreeTypeLibrary );
89 if( FT_Err_Ok != error )
91 DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
95 Length Shape( const Character* const text,
96 Length numberOfCharacters,
100 // Clear previoursly shaped texts.
103 mCharacterMap.Clear();
106 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
108 // Get the font's path file name from the font Id.
109 FontDescription fontDescription;
110 fontClient.GetDescription( fontId, fontDescription );
112 // Create a FreeType font's face.
114 FT_Error retVal = FT_New_Face( mFreeTypeLibrary, fontDescription.path.c_str(), 0u, &face );
115 if( FT_Err_Ok != retVal )
117 DALI_LOG_ERROR( "Failed to open face: %s\n", fontDescription.path.c_str() );
121 FT_Set_Pixel_Sizes( face, HIGH_QUALITY_PIXEL_SIZE, HIGH_QUALITY_PIXEL_SIZE );
123 /* Get our harfbuzz font struct */
124 hb_font_t* harfBuzzFont;
125 harfBuzzFont = hb_ft_font_create( face, NULL );
127 /* Create a buffer for harfbuzz to use */
128 hb_buffer_t* harfBuzzBuffer = hb_buffer_create();
130 const bool rtlDirection = IsRightToLeftScript( script );
131 hb_buffer_set_direction( harfBuzzBuffer,
132 rtlDirection ? HB_DIRECTION_RTL : HB_DIRECTION_LTR ); /* or LTR */
134 hb_buffer_set_script( harfBuzzBuffer,
135 SCRIPT_TO_HARFBUZZ[ script ] ); /* see hb-unicode.h */
137 hb_buffer_set_language( harfBuzzBuffer,
138 hb_language_from_string( DEFAULT_LANGUAGE,
139 DEFAULT_LANGUAGE_LENGTH ) );
141 /* Layout the text */
142 hb_buffer_add_utf32( harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters );
144 hb_shape( harfBuzzFont, harfBuzzBuffer, NULL, 0u );
147 unsigned int glyphCount;
148 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( harfBuzzBuffer, &glyphCount );
149 hb_glyph_position_t *glyphPositions = hb_buffer_get_glyph_positions( harfBuzzBuffer, &glyphCount );
151 const Length lastGlyphIndex = glyphCount - 1u;
152 for( Length i = 0u; i < glyphCount; ++i )
154 // If the direction is right to left, Harfbuzz retrieves the glyphs in the visual order.
155 // The glyphs are needed in the logical order to layout the text in lines.
156 const Length index = rtlDirection ? ( lastGlyphIndex - i ) : i;
158 mIndices.PushBack( glyphInfo[index].codepoint );
159 mAdvance.PushBack( glyphPositions[index].x_advance / TO_PIXELS );
160 mCharacterMap.PushBack( glyphInfo[index].cluster );
164 hb_buffer_destroy( harfBuzzBuffer );
165 hb_font_destroy( harfBuzzFont );
166 FT_Done_Face( face );
168 return mIndices.Count();
171 void GetGlyphs( GlyphInfo* glyphInfo,
172 CharacterIndex* glyphToCharacterMap )
174 Vector<CharacterIndex>::ConstIterator indicesIt = mIndices.Begin();
175 Vector<float>::ConstIterator advanceIt = mAdvance.Begin();
176 Vector<CharacterIndex>::ConstIterator characterMapIt = mCharacterMap.Begin();
178 for( Length index = 0u, size = mIndices.Count(); index < size; ++index )
180 GlyphInfo& glyph = *( glyphInfo + index );
181 CharacterIndex& glyphToCharacter = *( glyphToCharacterMap + index );
183 glyph.fontId = mFontId;
184 glyph.index = *( indicesIt + index );
185 glyph.advance = *( advanceIt + index );
187 glyphToCharacter = *( characterMapIt + index );
191 FT_Library mFreeTypeLibrary;
193 Vector<CharacterIndex> mIndices;
194 Vector<float> mAdvance;
195 Vector<CharacterIndex> mCharacterMap;
209 TextAbstraction::Shaping Shaping::Get()
211 TextAbstraction::Shaping shapingHandle;
213 SingletonService service( SingletonService::Get() );
216 // Check whether the singleton is already created
217 Dali::BaseHandle handle = service.GetSingleton( typeid( TextAbstraction::Shaping ) );
220 // If so, downcast the handle
221 Shaping* impl = dynamic_cast< Internal::Shaping* >( handle.GetObjectPtr() );
222 shapingHandle = TextAbstraction::Shaping( impl );
224 else // create and register the object
226 shapingHandle = TextAbstraction::Shaping( new Shaping );
227 service.Register( typeid( shapingHandle ), shapingHandle );
231 return shapingHandle;
234 Length Shaping::Shape( const Character* const text,
235 Length numberOfCharacters,
241 return mPlugin->Shape( text,
247 void Shaping::GetGlyphs( GlyphInfo* glyphInfo,
248 CharacterIndex* glyphToCharacterMap )
252 mPlugin->GetGlyphs( glyphInfo,
253 glyphToCharacterMap );
256 void Shaping::CreatePlugin()
260 mPlugin = new Plugin();
261 mPlugin->Initialize();
265 } // namespace Internal
267 } // namespace TextAbstraction