37 #if defined(ION_USE_ICU)
38 #include "third_party/icu/icu4c/source/common/unicode/udata.h"
39 #include "third_party/icu/icu4c/source/common/unicode/uloc.h"
40 #include "third_party/icu/icu4c/source/common/unicode/unistr.h"
41 #include "third_party/iculx_hb/include/layout/ParagraphLayout.h"
64 return c <= 255U && std::isspace(static_cast<char>(c));
72 static float ComputeLineWidth(
const FreeTypeFont& font,
73 const std::string& line) {
80 base::Utf8Iterator it(line);
84 const GlyphIndex g = font.GetDefaultGlyphForChar(c);
85 const FreeTypeFont::GlyphMetrics& glyph_metrics = font.GetGlyphMetrics(g);
91 const Vector2f kerning = font.GetKerning(prev_c, c);
94 x_max = x_min + glyph_metrics.bitmap_offset[0] + glyph_metrics.size[0];
95 x_min += glyph_metrics.advance[0];
104 const Lines& lines) {
110 const size_t num_lines = lines.size();
117 float first_line_above_baseline = 0.f;
124 first_line_above_baseline = std::max(
132 float last_line_below_baseline = 0.f;
138 last_line_below_baseline = std::max(
147 const float spacing =
148 options.
line_spacing *
static_cast<float>(num_lines - 1U);
150 first_line_above_baseline + last_line_below_baseline +
162 for (
size_t i = 0; i < num_lines; ++i) {
163 const std::string& line = lines[i];
164 const float line_width = ComputeLineWidth(font, line);
166 width = std::max(width, line_width);
176 static float ComputeVerticalAlignmentTranslation(
177 const LayoutOptions& options,
178 const TextSize& text_size,
float scale) {
181 float offset_in_pixels;
182 switch (options.vertical_alignment) {
184 offset_in_pixels = text_size.first_line_above_baseline;
187 offset_in_pixels = text_size.first_line_above_baseline -
188 0.5f * text_size.text_height_in_pixels;
192 offset_in_pixels = 0.0f;
196 text_size.first_line_above_baseline - text_size.text_height_in_pixels;
199 LOG(
ERROR) <<
"Invalid vertical alignment";
200 offset_in_pixels = 0.0f;
202 return options.target_point[1] - scale * offset_in_pixels;
207 static float ComputeHorizontalAlignmentTranslation(
208 const LayoutOptions& options,
float line_width_in_pixels,
209 float rect_width_in_pixels,
float scale) {
211 float offset_in_pixels;
212 switch (options.horizontal_alignment) {
214 offset_in_pixels = 0.0f;
217 offset_in_pixels = 0.5f * line_width_in_pixels;
220 offset_in_pixels = line_width_in_pixels;
223 LOG(
ERROR) <<
"Invalid horizontal alignment";
224 offset_in_pixels = 0.0f;
226 return options.target_point[0] - scale * offset_in_pixels;
244 if (target_size[0] == 0.0f) {
246 const float s = target_size[1] / rect_size[1];
247 transform_data.
scale.Set(s, s);
248 }
else if (target_size[1] == 0.0f) {
250 const float s = target_size[0] / rect_size[0];
251 transform_data.
scale.Set(s, s);
253 transform_data.
scale.Set(target_size[0] / rect_size[0],
254 target_size[1] / rect_size[1]);
259 const float y_translation = ComputeVerticalAlignmentTranslation(
260 options, text_size, transform_data.
scale[1]);
263 for (
size_t i = 0; i < num_lines; ++i) {
264 const float x_translation = ComputeHorizontalAlignmentTranslation(
266 rect_size[0], transform_data.
scale[0]);
274 return transform_data;
278 static const Layout::Quad BuildXyQuad(
const Range2f& rect) {
279 const Point2f& min = rect.GetMinPoint();
280 const Point2f& max = rect.GetMaxPoint();
281 return Layout::Quad(Point3f(min[0], min[1], 0.0f),
282 Point3f(max[0], min[1], 0.0f),
283 Point3f(max[0], max[1], 0.0f),
284 Point3f(min[0], max[1], 0.0f));
290 static void AddGlyphToLayout(
GlyphIndex glyph_index,
size_t line_index,
291 const Point2f& glyph_min,
292 const FreeTypeFont::GlyphMetrics& glyph_metrics,
293 const FreeTypeFontTransformData& transform_data,
294 size_t sdf_padding, Layout* layout) {
295 const Vector2f& glyph_size = glyph_metrics.size;
297 Range2f quad_rect = Range2f::BuildWithSize(
298 Point2f(glyph_min[0] * transform_data.scale[0],
299 glyph_min[1] * transform_data.scale[1]) +
300 transform_data.line_translations[line_index],
301 Vector2f(glyph_size[0] * transform_data.scale[0],
302 glyph_size[1] * transform_data.scale[1]));
303 const Range2f tight_bounds(quad_rect);
306 if (sdf_padding && (glyph_size[0] * glyph_size[1] != 0.f)) {
307 const float padding =
static_cast<float>(2 * sdf_padding);
308 const Vector2f
scale((glyph_size[0] + padding) / glyph_size[0],
309 (glyph_size[1] + padding) / glyph_size[1]);
313 glyph_metrics.bitmap_offset[0] * transform_data.scale[0],
316 (glyph_metrics.bitmap_offset[1] - glyph_metrics.size[1])
317 * transform_data.scale[1]);
319 CHECK(layout->AddGlyph(
320 Layout::Glyph(glyph_index, BuildXyQuad(quad_rect),
324 #if defined(ION_USE_ICU)
326 std::once_flag icu_initialize_once_flag;
330 static bool CheckIcuStatus(UErrorCode status) {
331 if (U_FAILURE(status)) {
332 LOG(
ERROR) <<
"ICU library error: " << u_errorName(status);
339 static void TryInitializeIcu(
bool* success) {
350 const std::string icu_data_directory =
351 #if defined(ION_PLATFORM_ANDROID)
353 #elif defined(ION_PLATFORM_MAC)
359 std::string icu_data;
361 for (
auto it = files.begin(); icu_data.empty() && it != files.end(); ++it) {
363 icu_data = icu_data_directory + *it;
365 if (icu_data.empty()) {
366 LOG(
ERROR) <<
"Unable to find ICU data file in: " << icu_data_directory;
369 port::MemoryMappedFile icu_mmap(icu_data);
370 if (!icu_mmap.GetData() || !icu_mmap.GetLength())
373 UErrorCode error = U_ZERO_ERROR;
374 udata_setAppData(icu_data.c_str(), icu_mmap.GetData(), &error);
375 CHECK(CheckIcuStatus(error));
380 static bool InitializeIcu() {
381 static bool icu_initialized =
false;
382 std::call_once(icu_initialize_once_flag,
383 std::bind(TryInitializeIcu, &icu_initialized));
384 return icu_initialized;
389 static void GetGlyphFromRun(
const iculx::ParagraphLayout::VisualRun& run,
390 int which_glyph_in_run, int32* glyph_index,
391 float* glyph_x,
float* glyph_y) {
392 *glyph_index = run.getGlyphs()[which_glyph_in_run];
393 *glyph_x = run.getPositions()[which_glyph_in_run * 2];
394 *glyph_y = -run.getPositions()[which_glyph_in_run * 2 + 1];
399 static float IcuLayoutEngineLayoutLine(
400 const FreeTypeFont& font,
401 const std::string&
text,
403 const FreeTypeFontTransformData& transform_data,
405 if (!InitializeIcu()) {
410 icu::UnicodeString chars = icu::UnicodeString::fromUTF8(text);
411 if (chars.isEmpty()) {
412 DLOG(
ERROR) <<
"Empty text for layout, or corrupt utf8? [" << text <<
"]";
417 iculx::FontRuns runs(0);
418 font.GetFontRunsForText(chars, &runs);
419 LEErrorCode status = LE_NO_ERROR;
420 std::unique_ptr<iculx::ParagraphLayout> icu_layout(
new iculx::ParagraphLayout(
421 chars.getBuffer(), chars.length(), &runs, NULL, NULL, NULL,
422 UBIDI_DEFAULT_LTR,
false , status));
423 if (status != LE_NO_ERROR) {
424 DLOG(
ERROR) <<
"new ParagraphLayout error: " << status;
430 icu_layout->reflow();
431 std::unique_ptr<iculx::ParagraphLayout::Line> line(icu_layout->nextLine(0));
436 enum { kImpossibleGlyphIndex = -1 };
437 int32 glyph_id = kImpossibleGlyphIndex;
441 if (layout != NULL) {
442 layout->Reserve(chars.length());
443 for (
int i = 0; i < line->countRuns(); ++i) {
444 const iculx::ParagraphLayout::VisualRun *run = line->getVisualRun(i);
445 const icu::LEFontInstance* run_font = run->getFont();
446 for (
int j = 0; j < run->getGlyphCount(); ++j) {
447 GetGlyphFromRun(*run, j, &glyph_id, &glyph_x, &glyph_y);
448 if (glyph_id == 0 || glyph_id >= 0xffff)
450 GlyphIndex glyph_index = font.GlyphIndexForICUFont(run_font, glyph_id);
451 const FreeTypeFont::GlyphMetrics& metrics =
452 font.GetGlyphMetrics(glyph_index);
455 glyph_x += metrics.bitmap_offset[0];
456 glyph_y += transform_data.line_y_offset_in_pixels *
457 static_cast<float>(line_index) +
458 (metrics.bitmap_offset[1] - metrics.size[1]);
459 AddGlyphToLayout(glyph_index, line_index, Point2f(glyph_x, glyph_y),
460 metrics, transform_data,
461 font.GetSdfPadding(), layout);
466 for (
int i = line->countRuns() - 1;
467 i >= 0 && glyph_id == kImpossibleGlyphIndex; --i) {
468 const iculx::ParagraphLayout::VisualRun *run = line->getVisualRun(i);
469 for (
int j = run->getGlyphCount() - 1; j >= 0; --j) {
470 if (run->getGlyphs()[j] < 0xffff) {
471 GetGlyphFromRun(*run, j, &glyph_id, &glyph_x, &glyph_y);
478 if (glyph_id == kImpossibleGlyphIndex) {
484 runs.getFont(runs.getCount() - 1)->getGlyphAdvance(glyph_id, advance_p);
485 float final_advance = advance_p.fX;
486 float final_position = glyph_x;
487 return final_advance + final_position;
495 static bool IsInFastUnicodeRange(
const std::string& text) {
498 static const CharIndex g_fast_unicode_ranges[] = {
510 const CharIndex* begin = g_fast_unicode_ranges;
511 const CharIndex* end = begin + arraysize(g_fast_unicode_ranges);
512 base::Utf8Iterator it(text);
515 const CharIndex* search = std::upper_bound(begin, end, c);
519 if (((search - begin) & 1) == 0)
return false;
525 static bool IsInFastUnicodeRange(
const std::string& text) {
return true; }
529 static float IcuLayoutEngineLayoutLine(
530 const FreeTypeFont& font,
531 const std::string& text,
533 const FreeTypeFontTransformData& transform_data,
538 #endif // ION_USE_ICU
541 static void SimpleLayOutLine(
542 const FreeTypeFont& font,
543 const std::string& line,
545 const FreeTypeFontTransformData& transform_data,
548 base::Utf8Iterator it(line);
552 const GlyphIndex g = font.GetDefaultGlyphForChar(c);
553 const FreeTypeFont::GlyphMetrics& glyph_metrics = font.GetGlyphMetrics(g);
556 }
else if (IsSpace(c)) {
557 x_min += glyph_metrics.advance[0];
560 transform_data.line_y_offset_in_pixels *
561 static_cast<float>(line_index) +
562 (glyph_metrics.bitmap_offset[1] - glyph_metrics.size[1]);
564 const Vector2f kerning = font.GetKerning(prev_c, c);
568 Point2f glyph_min(x_min + glyph_metrics.bitmap_offset[0], y_min);
569 AddGlyphToLayout(g, line_index, glyph_min, glyph_metrics, transform_data,
570 font.GetSdfPadding(), layout);
571 x_min += glyph_metrics.advance[0];
581 const size_t num_lines = lines.size();
585 for (
size_t i = 0; i < num_lines; ++i) {
586 if (use_icu && !IsInFastUnicodeRange(lines[i])) {
587 IcuLayoutEngineLayoutLine(
588 font, lines[i], i, transform_data, &layout);
590 SimpleLayOutLine(font, lines[i], i, transform_data, &layout);
static const uint32 kInvalidCharIndex
An invalid Unicode character index.
bool IsInvalidReference(const T &value)
IsInvalidReference() returns true if a passed const reference of type T has an address of InvalidRefe...
void SetLineAdvanceHeight(float line_advance)
Sets the vertical distance between successive baselines.
const std::string GetEnvironmentVariableValue(const std::string &name)
Returns the value of the named environment variable.
std::vector< std::string > Lines
Lines of text from a single string (usually split on ' ').
std::vector< float > line_widths_in_pixels
Width of each line of text in pixels.
bool StartsWith(const std::string &target, const std::string &start)
Returns whether target begins with start.
const FreeTypeFontTransformData ComputeTransformData(const Font &font, const LayoutOptions &options, const TextSize &text_size)
Sets the scale and translation fields of the LayoutData instance with the scale and translation requi...
math::Vector2f size
Width and height of the glyph, in pixels.
math::Vector2f rect_size_in_pixels
Size of the entire text rectangle in pixels.
This derived Font class represents a FreeType2 font.
GlyphIndex GetDefaultGlyphForChar(CharIndex char_index) const override
Font overrides.
const Layout LayOutText(const FreeTypeFont &font, bool use_icu, const Lines &lines, const FreeTypeFontTransformData &transform_data)
Returns a Layout populated by glyphs representing lines of text.
std::vector< std::string > ListDirectory(const std::string &path)
Returns the contents of path, non-recursively.
#define DLOG(severity)
Same as LOG(severity), but only logs in debug mode.
float line_advance_height
Nominal font-wide line-advance height, in pixels.
#define LOG(severity)
Logs the streamed message unconditionally with a severity of severity.
#define DCHECK_GT(val1, val2)
std::vector< math::Vector2f > line_translations
Translation to apply to position glyphs for each line of text.
Range< 2, float > Range2f
The Utf8Iterator class iterates over characters in strings encoded with UTF-8, extracting the Unicode...
uint32 CharIndex
Typedef for a Unicode index of a character.
size_t GetSizeInPixels() const
Returns the size of the font in pixels.
This struct defines parameters affecting layout of a single text string when passed to BuildLayout()...
float text_height_in_pixels
Height of the text inside the rectangle in pixels.
float first_line_above_baseline
Max height above baseline of the first line of text (depends on contents!).
const FontMetrics & GetFontMetrics() const
Returns the FontMetrics for the font.
math::Vector2f scale
Scale to apply to resize glyphs.
float line_height_in_pixels
Height of a single line of text in pixels.
const TextSize ComputeTextSize(const FreeTypeFont &font, const LayoutOptions &options, const Lines &lines)
Computes the size of text and returns it as a TextSize instance.
math::Vector2f target_size
Target width and height of the text rectangle. (Default: 0 in x, 1 in y)
A Layout instance specifies how glyphs are arranged to form text.
float line_y_offset_in_pixels
How much to translate each successive line in y, in pixels.
bool EndsWith(const std::string &target, const std::string &end)
Returns whether target ends with end.
TextSize contains information about the size of multi-line text.
math::Vector2f bitmap_offset
Distance in X and Y from the baseline to the top left pixel of the glyph bitmap, in pixels...
float line_spacing
Spacing between baselines of lines of multi-line text, expressed as a fraction of the font's FontMetr...
This struct represents the metrics for a single glyph.
This contains the values needed to transform glyph rectangles into the correct coordinates.
const Range< Dimension, T > ScaleRangeNonUniformly(const Range< Dimension, T > &r, const Vector< Dimension, T > scale_factors)
Returns a Range that is the input Range scaled nonuniformly about its center by the given per-dimensi...
This struct represents the cumulative metrics for the font.
const GlyphMetrics & GetGlyphMetrics(GlyphIndex glyph_index) const
Returns the GlyphMetrics for a glyph.
Font is a base class for implementation-specific representations of fonts.