2 * Copyright (c) 2021 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/devel-api/text-abstraction/font-client.h>
23 #include <dali/devel-api/text-abstraction/glyph-info.h>
24 #include <dali/integration-api/debug.h>
25 #include "font-client-impl.h"
28 #include <dali/devel-api/common/singleton-service.h>
29 #include <harfbuzz/hb-ft.h>
30 #include <harfbuzz/hb.h>
34 #if defined(DEBUG_ENABLED)
35 Dali::Integration::Log::Filter* gLogFilter = Dali::Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FONT_CLIENT");
42 namespace TextAbstraction
46 const char* const DEFAULT_LANGUAGE = "en";
47 const unsigned int DEFAULT_LANGUAGE_LENGTH = 2u;
48 const float FROM_266 = 1.0f / 64.0f;
50 const hb_script_t SCRIPT_TO_HARFBUZZ[] =
54 HB_SCRIPT_COMMON, // ASCII_DIGITS
55 HB_SCRIPT_COMMON, // ASCII_PS
57 HB_SCRIPT_COMMON, // C1_CONTROLS
58 HB_SCRIPT_COMMON, // C1_PS
59 HB_SCRIPT_COMMON, // C1_MATH
60 HB_SCRIPT_COMMON, // SML_P
61 HB_SCRIPT_COMMON, // PHONETIC_U
62 HB_SCRIPT_COMMON, // PHONETIC_SS
63 HB_SCRIPT_COMMON, // NUMERIC_SS
64 HB_SCRIPT_COMMON, // LETTER_LIKE
65 HB_SCRIPT_COMMON, // NUMBER_FORMS
66 HB_SCRIPT_COMMON, // FRACTIONS_NF
67 HB_SCRIPT_COMMON, // NON_LATIN_LED
68 HB_SCRIPT_COMMON, // HWFW_S
107 HB_SCRIPT_MEETEI_MAYEK,
109 HB_SCRIPT_UNKNOWN, // EMOJI
110 HB_SCRIPT_UNKNOWN, // SYMBOLS1
111 HB_SCRIPT_UNKNOWN, // SYMBOLS2
112 HB_SCRIPT_UNKNOWN, // SYMBOLS3
113 HB_SCRIPT_UNKNOWN, // SYMBOLS4
114 HB_SCRIPT_UNKNOWN, // SYMBOLS5
117 struct Shaping::Plugin
131 Length Shape(const Character* const text,
132 Length numberOfCharacters,
136 // Clear previoursly shaped texts.
139 mCharacterMap.Clear();
143 TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
144 TextAbstraction::Internal::FontClient& fontClientImpl = TextAbstraction::GetImplementation(fontClient);
146 const FontDescription::Type type = fontClientImpl.GetFontType(fontId);
150 case FontDescription::FACE_FONT:
152 // Reserve some space to avoid reallocations.
153 const Length numberOfGlyphs = static_cast<Length>(1.3f * static_cast<float>(numberOfCharacters));
154 mIndices.Reserve(numberOfGlyphs);
155 mAdvance.Reserve(numberOfGlyphs);
156 mCharacterMap.Reserve(numberOfGlyphs);
157 mOffset.Reserve(2u * numberOfGlyphs);
159 // Retrieve a FreeType font's face.
160 FT_Face face = fontClientImpl.GetFreetypeFace(fontId);
163 // Nothing to do if the face is null.
167 unsigned int horizontalDpi = 0u;
168 unsigned int verticalDpi = 0u;
169 fontClient.GetDpi(horizontalDpi, verticalDpi);
171 FT_Set_Char_Size(face,
173 fontClient.GetPointSize(fontId),
177 /* Get our harfbuzz font struct */
178 hb_font_t* harfBuzzFont;
179 harfBuzzFont = hb_ft_font_create(face, NULL);
181 /* Create a buffer for harfbuzz to use */
182 hb_buffer_t* harfBuzzBuffer = hb_buffer_create();
184 const bool rtlDirection = IsRightToLeftScript(script);
185 hb_buffer_set_direction(harfBuzzBuffer,
186 rtlDirection ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); /* or LTR */
188 hb_buffer_set_script(harfBuzzBuffer,
189 SCRIPT_TO_HARFBUZZ[script]); /* see hb-unicode.h */
191 char* currentLocale = setlocale(LC_MESSAGES, NULL);
193 std::istringstream stringStream(currentLocale);
194 std::string localeString;
195 std::getline(stringStream, localeString, '_');
196 hb_buffer_set_language(harfBuzzBuffer, hb_language_from_string(localeString.c_str(), localeString.size()));
198 /* Layout the text */
199 hb_buffer_add_utf32(harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters);
201 hb_shape(harfBuzzFont, harfBuzzBuffer, NULL, 0u);
204 unsigned int glyphCount;
205 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos(harfBuzzBuffer, &glyphCount);
206 hb_glyph_position_t* glyphPositions = hb_buffer_get_glyph_positions(harfBuzzBuffer, &glyphCount);
207 const GlyphIndex lastGlyphIndex = glyphCount - 1u;
209 for(GlyphIndex i = 0u; i < glyphCount;)
213 // If the direction is right to left, Harfbuzz retrieves the glyphs in the visual order.
214 // The glyphs are needed in the logical order to layout the text in lines.
215 // Do not change the order of the glyphs if they belong to the same cluster.
216 GlyphIndex rtlIndex = lastGlyphIndex - i;
218 unsigned int cluster = glyphInfo[rtlIndex].cluster;
219 unsigned int previousCluster = cluster;
220 Length numberOfGlyphsInCluster = 0u;
222 while((cluster == previousCluster))
224 ++numberOfGlyphsInCluster;
225 previousCluster = cluster;
231 cluster = glyphInfo[rtlIndex].cluster;
239 rtlIndex = lastGlyphIndex - (i + (numberOfGlyphsInCluster - 1u));
241 for(GlyphIndex j = 0u; j < numberOfGlyphsInCluster; ++j)
243 const GlyphIndex index = rtlIndex + j;
245 mIndices.PushBack(glyphInfo[index].codepoint);
246 mAdvance.PushBack(glyphPositions[index].x_advance * FROM_266);
247 mCharacterMap.PushBack(glyphInfo[index].cluster);
248 mOffset.PushBack(glyphPositions[index].x_offset * FROM_266);
249 mOffset.PushBack(glyphPositions[index].y_offset * FROM_266);
252 i += numberOfGlyphsInCluster;
256 mIndices.PushBack(glyphInfo[i].codepoint);
257 mAdvance.PushBack(glyphPositions[i].x_advance * FROM_266);
258 mCharacterMap.PushBack(glyphInfo[i].cluster);
259 mOffset.PushBack(glyphPositions[i].x_offset * FROM_266);
260 mOffset.PushBack(glyphPositions[i].y_offset * FROM_266);
267 hb_buffer_destroy(harfBuzzBuffer);
268 hb_font_destroy(harfBuzzFont);
271 case FontDescription::BITMAP_FONT:
273 // Reserve some space to avoid reallocations.
274 // The advance and offset tables can be initialized with zeros as it's not needed to get metrics from the bitmaps here.
275 mIndices.Resize(numberOfCharacters);
276 mAdvance.Resize(numberOfCharacters, 0u);
277 mCharacterMap.Reserve(numberOfCharacters);
278 mOffset.Resize(2u * numberOfCharacters, 0.f);
280 // The utf32 character can be used as the glyph's index.
281 std::copy(text, text + numberOfCharacters, mIndices.Begin());
283 // The glyph to character map is 1 to 1.
284 for(unsigned int index = 0u; index < numberOfCharacters; ++index)
286 mCharacterMap.PushBack(index);
292 DALI_LOG_INFO(gLogFilter, Debug::General, " Invalid type of font\n");
296 return mIndices.Count();
299 void GetGlyphs(GlyphInfo* glyphInfo,
300 CharacterIndex* glyphToCharacterMap)
302 Vector<CharacterIndex>::ConstIterator indicesIt = mIndices.Begin();
303 Vector<float>::ConstIterator advanceIt = mAdvance.Begin();
304 Vector<float>::ConstIterator offsetIt = mOffset.Begin();
305 Vector<CharacterIndex>::ConstIterator characterMapIt = mCharacterMap.Begin();
307 for(GlyphIndex index = 0u, size = mIndices.Count(); index < size; ++index)
309 GlyphInfo& glyph = *(glyphInfo + index);
310 CharacterIndex& glyphToCharacter = *(glyphToCharacterMap + index);
312 glyph.fontId = mFontId;
313 glyph.index = *(indicesIt + index);
314 glyph.advance = *(advanceIt + index);
316 const GlyphIndex offsetIndex = 2u * index;
317 glyph.xBearing = *(offsetIt + offsetIndex);
318 glyph.yBearing = *(offsetIt + offsetIndex + 1u);
320 glyphToCharacter = *(characterMapIt + index);
324 Vector<CharacterIndex> mIndices;
325 Vector<float> mAdvance;
326 Vector<float> mOffset;
327 Vector<CharacterIndex> mCharacterMap;
341 TextAbstraction::Shaping Shaping::Get()
343 TextAbstraction::Shaping shapingHandle;
345 SingletonService service(SingletonService::Get());
348 // Check whether the singleton is already created
349 Dali::BaseHandle handle = service.GetSingleton(typeid(TextAbstraction::Shaping));
352 // If so, downcast the handle
353 Shaping* impl = dynamic_cast<Internal::Shaping*>(handle.GetObjectPtr());
354 shapingHandle = TextAbstraction::Shaping(impl);
356 else // create and register the object
358 shapingHandle = TextAbstraction::Shaping(new Shaping);
359 service.Register(typeid(shapingHandle), shapingHandle);
363 return shapingHandle;
366 Length Shaping::Shape(const Character* const text,
367 Length numberOfCharacters,
373 return mPlugin->Shape(text,
379 void Shaping::GetGlyphs(GlyphInfo* glyphInfo,
380 CharacterIndex* glyphToCharacterMap)
384 mPlugin->GetGlyphs(glyphInfo,
385 glyphToCharacterMap);
388 void Shaping::CreatePlugin()
392 mPlugin = new Plugin();
396 } // namespace Internal
398 } // namespace TextAbstraction