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