blob: 5e9db956d26d28135235f9fce1346d40e7ec223c [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/html/canvas/text_metrics.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_baselines.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h"
#include "third_party/blink/renderer/platform/fonts/character_range.h"
namespace blink {
constexpr int kHangingAsPercentOfAscent = 80;
float TextMetrics::GetFontBaseline(const TextBaseline& text_baseline,
const SimpleFontData& font_data) {
FontMetrics font_metrics = font_data.GetFontMetrics();
switch (text_baseline) {
case kTopTextBaseline:
return font_data.NormalizedTypoAscent().ToFloat();
case kHangingTextBaseline:
// According to
// http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
// "FOP (Formatting Objects Processor) puts the hanging baseline at 80% of
// the ascender height"
return font_metrics.FloatAscent() * kHangingAsPercentOfAscent / 100.0;
case kIdeographicTextBaseline:
return -font_metrics.FloatDescent();
case kBottomTextBaseline:
return -font_data.NormalizedTypoDescent().ToFloat();
case kMiddleTextBaseline: {
const FontHeight metrics = font_data.NormalizedTypoAscentAndDescent();
return (metrics.ascent.ToFloat() - metrics.descent.ToFloat()) / 2.0f;
}
case kAlphabeticTextBaseline:
default:
// Do nothing.
break;
}
return 0;
}
void TextMetrics::Trace(Visitor* visitor) const {
visitor->Trace(baselines_);
ScriptWrappable::Trace(visitor);
}
TextMetrics::TextMetrics() : baselines_(Baselines::Create()) {}
TextMetrics::TextMetrics(const Font& font,
const TextDirection& direction,
const TextBaseline& baseline,
const TextAlign& align,
const String& text)
: TextMetrics() {
Update(font, direction, baseline, align, text);
}
void TextMetrics::Update(const Font& font,
const TextDirection& direction,
const TextBaseline& baseline,
const TextAlign& align,
const String& text) {
const SimpleFontData* font_data = font.PrimaryFont();
if (!font_data)
return;
{
// TODO(kojii): Need to figure out the desired behavior of |advances| when
// bidi reorder occurs.
TextRun text_run(
text, /* xpos */ 0, /* expansion */ 0,
TextRun::kAllowTrailingExpansion | TextRun::kForbidLeadingExpansion,
direction, false);
text_run.SetNormalizeSpace(true);
advances_ = font.IndividualCharacterAdvances(text_run);
}
// x direction
// Run bidi algorithm on the given text. Step 5 of:
// https://html.spec.whatwg.org/multipage/canvas.html#text-preparation-algorithm
FloatRect glyph_bounds;
String text16 = text;
text16.Ensure16Bit();
NGBidiParagraph bidi;
bidi.SetParagraph(text16, direction);
NGBidiParagraph::Runs runs;
bidi.GetLogicalRuns(text16, &runs);
float xpos = 0;
for (const auto& run : runs) {
// Measure each run.
TextRun text_run(
StringView(text, run.start, run.Length()), xpos, /* expansion */ 0,
TextRun::kAllowTrailingExpansion | TextRun::kForbidLeadingExpansion,
run.Direction(), /* directional_override */ false);
text_run.SetNormalizeSpace(true);
FloatRect run_glyph_bounds;
float run_width = font.Width(text_run, nullptr, &run_glyph_bounds);
// Accumulate the position and the glyph bounding box.
run_glyph_bounds.Move(xpos, 0);
glyph_bounds.Unite(run_glyph_bounds);
xpos += run_width;
}
double real_width = xpos;
width_ = real_width;
float dx = 0.0f;
if (align == kCenterTextAlign)
dx = real_width / 2.0f;
else if (align == kRightTextAlign ||
(align == kStartTextAlign && direction == TextDirection::kRtl) ||
(align == kEndTextAlign && direction != TextDirection::kRtl))
dx = real_width;
actual_bounding_box_left_ = -glyph_bounds.X() + dx;
actual_bounding_box_right_ = glyph_bounds.MaxX() - dx;
// y direction
const FontMetrics& font_metrics = font_data->GetFontMetrics();
const float ascent = font_metrics.FloatAscent();
const float descent = font_metrics.FloatDescent();
const float baseline_y = GetFontBaseline(baseline, *font_data);
font_bounding_box_ascent_ = ascent - baseline_y;
font_bounding_box_descent_ = descent + baseline_y;
actual_bounding_box_ascent_ = -glyph_bounds.Y() - baseline_y;
actual_bounding_box_descent_ = glyph_bounds.MaxY() + baseline_y;
// TODO(kojii): We use normalized sTypoAscent/Descent here, but this should be
// revisited when the spec evolves.
const FontHeight normalized_typo_metrics =
font_data->NormalizedTypoAscentAndDescent();
em_height_ascent_ = normalized_typo_metrics.ascent - baseline_y;
em_height_descent_ = normalized_typo_metrics.descent + baseline_y;
// TODO(fserb): hanging/ideographic baselines are broken.
baselines_->setAlphabetic(-baseline_y);
baselines_->setHanging(ascent * kHangingAsPercentOfAscent / 100.0f -
baseline_y);
baselines_->setIdeographic(-descent - baseline_y);
}
} // namespace blink