1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/html/canvas/text_metrics.h"
6 #include "third_party/blink/renderer/bindings/core/v8/v8_baselines.h"
7 #include "third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h"
8 #include "third_party/blink/renderer/platform/fonts/character_range.h"
9 
10 namespace blink {
11 
12 constexpr int kHangingAsPercentOfAscent = 80;
13 
GetFontBaseline(const TextBaseline & text_baseline,const SimpleFontData & font_data)14 float TextMetrics::GetFontBaseline(const TextBaseline& text_baseline,
15                                    const SimpleFontData& font_data) {
16   FontMetrics font_metrics = font_data.GetFontMetrics();
17   switch (text_baseline) {
18     case kTopTextBaseline:
19       return font_data.NormalizedTypoAscent().ToFloat();
20     case kHangingTextBaseline:
21       // According to
22       // http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
23       // "FOP (Formatting Objects Processor) puts the hanging baseline at 80% of
24       // the ascender height"
25       return font_metrics.FloatAscent() * kHangingAsPercentOfAscent / 100.0;
26     case kIdeographicTextBaseline:
27       return -font_metrics.FloatDescent();
28     case kBottomTextBaseline:
29       return -font_data.NormalizedTypoDescent().ToFloat();
30     case kMiddleTextBaseline: {
31       const FontHeight metrics = font_data.NormalizedTypoAscentAndDescent();
32       return (metrics.ascent.ToFloat() - metrics.descent.ToFloat()) / 2.0f;
33     }
34     case kAlphabeticTextBaseline:
35     default:
36       // Do nothing.
37       break;
38   }
39   return 0;
40 }
41 
Trace(Visitor * visitor) const42 void TextMetrics::Trace(Visitor* visitor) const {
43   visitor->Trace(baselines_);
44   ScriptWrappable::Trace(visitor);
45 }
46 
TextMetrics()47 TextMetrics::TextMetrics() : baselines_(Baselines::Create()) {}
48 
TextMetrics(const Font & font,const TextDirection & direction,const TextBaseline & baseline,const TextAlign & align,const String & text)49 TextMetrics::TextMetrics(const Font& font,
50                          const TextDirection& direction,
51                          const TextBaseline& baseline,
52                          const TextAlign& align,
53                          const String& text)
54     : TextMetrics() {
55   Update(font, direction, baseline, align, text);
56 }
57 
Update(const Font & font,const TextDirection & direction,const TextBaseline & baseline,const TextAlign & align,const String & text)58 void TextMetrics::Update(const Font& font,
59                          const TextDirection& direction,
60                          const TextBaseline& baseline,
61                          const TextAlign& align,
62                          const String& text) {
63   const SimpleFontData* font_data = font.PrimaryFont();
64   if (!font_data)
65     return;
66 
67   {
68     // TODO(kojii): Need to figure out the desired behavior of |advances| when
69     // bidi reorder occurs.
70     TextRun text_run(
71         text, /* xpos */ 0, /* expansion */ 0,
72         TextRun::kAllowTrailingExpansion | TextRun::kForbidLeadingExpansion,
73         direction, false);
74     text_run.SetNormalizeSpace(true);
75     advances_ = font.IndividualCharacterAdvances(text_run);
76   }
77 
78   // x direction
79   // Run bidi algorithm on the given text. Step 5 of:
80   // https://html.spec.whatwg.org/multipage/canvas.html#text-preparation-algorithm
81   FloatRect glyph_bounds;
82   String text16 = text;
83   text16.Ensure16Bit();
84   NGBidiParagraph bidi;
85   bidi.SetParagraph(text16, direction);
86   NGBidiParagraph::Runs runs;
87   bidi.GetLogicalRuns(text16, &runs);
88   float xpos = 0;
89   for (const auto& run : runs) {
90     // Measure each run.
91     TextRun text_run(
92         StringView(text, run.start, run.Length()), xpos, /* expansion */ 0,
93         TextRun::kAllowTrailingExpansion | TextRun::kForbidLeadingExpansion,
94         run.Direction(), /* directional_override */ false);
95     text_run.SetNormalizeSpace(true);
96     FloatRect run_glyph_bounds;
97     float run_width = font.Width(text_run, nullptr, &run_glyph_bounds);
98 
99     // Accumulate the position and the glyph bounding box.
100     run_glyph_bounds.Move(xpos, 0);
101     glyph_bounds.Unite(run_glyph_bounds);
102     xpos += run_width;
103   }
104   double real_width = xpos;
105   width_ = real_width;
106 
107   float dx = 0.0f;
108   if (align == kCenterTextAlign)
109     dx = real_width / 2.0f;
110   else if (align == kRightTextAlign ||
111            (align == kStartTextAlign && direction == TextDirection::kRtl) ||
112            (align == kEndTextAlign && direction != TextDirection::kRtl))
113     dx = real_width;
114   actual_bounding_box_left_ = -glyph_bounds.X() + dx;
115   actual_bounding_box_right_ = glyph_bounds.MaxX() - dx;
116 
117   // y direction
118   const FontMetrics& font_metrics = font_data->GetFontMetrics();
119   const float ascent = font_metrics.FloatAscent();
120   const float descent = font_metrics.FloatDescent();
121   const float baseline_y = GetFontBaseline(baseline, *font_data);
122   font_bounding_box_ascent_ = ascent - baseline_y;
123   font_bounding_box_descent_ = descent + baseline_y;
124   actual_bounding_box_ascent_ = -glyph_bounds.Y() - baseline_y;
125   actual_bounding_box_descent_ = glyph_bounds.MaxY() + baseline_y;
126   // TODO(kojii): We use normalized sTypoAscent/Descent here, but this should be
127   // revisited when the spec evolves.
128   const FontHeight normalized_typo_metrics =
129       font_data->NormalizedTypoAscentAndDescent();
130   em_height_ascent_ = normalized_typo_metrics.ascent - baseline_y;
131   em_height_descent_ = normalized_typo_metrics.descent + baseline_y;
132 
133   // TODO(fserb): hanging/ideographic baselines are broken.
134   baselines_->setAlphabetic(-baseline_y);
135   baselines_->setHanging(ascent * kHangingAsPercentOfAscent / 100.0f -
136                          baseline_y);
137   baselines_->setIdeographic(-descent - baseline_y);
138 }
139 
140 }  // namespace blink
141