blob: e51374bf269954915622219bb4291395c3b745f1 [file] [log] [blame]
// Copyright 2016 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/platform/fonts/shaping/shape_result_buffer.h"
#include "third_party/blink/renderer/platform/fonts/character_range.h"
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
#include "third_party/blink/renderer/platform/geometry/float_point.h"
#include "third_party/blink/renderer/platform/text/text_direction.h"
namespace blink {
namespace {
unsigned CharactersInShapeResult(
const Vector<scoped_refptr<const ShapeResult>, 64>& results) {
unsigned num_characters = 0;
for (const scoped_refptr<const ShapeResult>& result : results)
num_characters += result->NumCharacters();
return num_characters;
}
} // namespace
CharacterRange ShapeResultBuffer::GetCharacterRange(
const StringView& text,
TextDirection direction,
float total_width,
unsigned absolute_from,
unsigned absolute_to) const {
DCHECK_EQ(CharactersInShapeResult(results_), text.length());
float current_x = 0;
float from_x = 0;
float to_x = 0;
bool found_from_x = false;
bool found_to_x = false;
float min_y = 0;
float max_y = 0;
if (direction == TextDirection::kRtl)
current_x = total_width;
// The absoluteFrom and absoluteTo arguments represent the start/end offset
// for the entire run, from/to are continuously updated to be relative to
// the current word (ShapeResult instance).
int from = absolute_from;
int to = absolute_to;
unsigned total_num_characters = 0;
for (unsigned j = 0; j < results_.size(); j++) {
const scoped_refptr<const ShapeResult> result = results_[j];
result->EnsureGraphemes(
StringView(text, total_num_characters, result->NumCharacters()));
if (direction == TextDirection::kRtl) {
// Convert logical offsets to visual offsets, because results are in
// logical order while runs are in visual order.
if (!found_from_x && from >= 0 &&
static_cast<unsigned>(from) < result->NumCharacters())
from = result->NumCharacters() - from - 1;
if (!found_to_x && to >= 0 &&
static_cast<unsigned>(to) < result->NumCharacters())
to = result->NumCharacters() - to - 1;
current_x -= result->Width();
}
for (unsigned i = 0; i < result->runs_.size(); i++) {
if (!result->runs_[i])
continue;
DCHECK_EQ(direction == TextDirection::kRtl, result->runs_[i]->IsRtl());
int num_characters = result->runs_[i]->num_characters_;
if (!found_from_x && from >= 0 && from < num_characters) {
from_x = result->runs_[i]->XPositionForVisualOffset(
from, AdjustMidCluster::kToStart) +
current_x;
found_from_x = true;
} else {
from -= num_characters;
}
if (!found_to_x && to >= 0 && to < num_characters) {
to_x = result->runs_[i]->XPositionForVisualOffset(
to, AdjustMidCluster::kToEnd) +
current_x;
found_to_x = true;
} else {
to -= num_characters;
}
if (found_from_x || found_to_x) {
min_y = std::min(min_y, result->DeprecatedInkBounds().Y());
max_y = std::max(max_y, result->DeprecatedInkBounds().MaxY());
}
if (found_from_x && found_to_x)
break;
current_x += result->runs_[i]->width_;
}
if (direction == TextDirection::kRtl)
current_x -= result->Width();
total_num_characters += result->NumCharacters();
}
// The position in question might be just after the text.
if (!found_from_x && absolute_from == total_num_characters) {
from_x = direction == TextDirection::kRtl ? 0 : total_width;
found_from_x = true;
}
if (!found_to_x && absolute_to == total_num_characters) {
to_x = direction == TextDirection::kRtl ? 0 : total_width;
found_to_x = true;
}
if (!found_from_x)
from_x = 0;
if (!found_to_x)
to_x = direction == TextDirection::kRtl ? 0 : total_width;
// None of our runs is part of the selection, possibly invalid arguments.
if (!found_to_x && !found_from_x)
from_x = to_x = 0;
if (from_x < to_x)
return CharacterRange(from_x, to_x, -min_y, max_y);
return CharacterRange(to_x, from_x, -min_y, max_y);
}
Vector<CharacterRange> ShapeResultBuffer::IndividualCharacterRanges(
TextDirection direction,
float total_width) const {
Vector<CharacterRange> ranges;
float current_x = direction == TextDirection::kRtl ? total_width : 0;
for (const scoped_refptr<const ShapeResult>& result : results_)
current_x = result->IndividualCharacterRanges(&ranges, current_x);
return ranges;
}
void ShapeResultBuffer::AddRunInfoAdvances(const ShapeResult::RunInfo& run_info,
double offset,
Vector<double>& advances) {
const unsigned num_glyphs = run_info.glyph_data_.size();
const unsigned num_chars = run_info.num_characters_;
if (run_info.IsRtl())
offset += run_info.width_;
double current_width = 0;
for (unsigned glyph_id = 0; glyph_id < num_glyphs; glyph_id++) {
unsigned gid = run_info.IsRtl() ? num_glyphs - glyph_id - 1 : glyph_id;
unsigned next_gid =
run_info.IsRtl() ? num_glyphs - glyph_id - 2 : glyph_id + 1;
const HarfBuzzRunGlyphData& glyph = run_info.glyph_data_[gid];
unsigned char_id = glyph.character_index;
unsigned next_char_id =
(glyph_id + 1 == num_glyphs)
? num_chars
: run_info.glyph_data_[next_gid].character_index;
current_width += glyph.advance;
if (char_id == next_char_id)
continue;
unsigned num_graphemes = run_info.NumGraphemes(char_id, next_char_id);
for (unsigned i = char_id; i < next_char_id; i++) {
if (run_info.IsRtl()) {
advances.push_back(offset - (current_width / num_graphemes));
} else {
advances.push_back(offset);
}
if (num_graphemes == next_char_id - char_id) {
offset += (current_width / num_graphemes) * (run_info.IsRtl() ? -1 : 1);
}
}
if (num_graphemes != next_char_id - char_id) {
offset += current_width * (run_info.IsRtl() ? -1 : 1);
}
current_width = 0;
}
}
Vector<double> ShapeResultBuffer::IndividualCharacterAdvances(
const StringView& text,
TextDirection direction,
float total_width) const {
unsigned character_offset = 0;
Vector<double> advances;
double current_x = direction == TextDirection::kRtl ? total_width : 0;
for (const scoped_refptr<const ShapeResult>& result : results_) {
unsigned run_count = result->runs_.size();
result->EnsureGraphemes(
StringView(text, character_offset, result->NumCharacters()));
if (result->IsRtl()) {
for (int index = run_count - 1; index >= 0; index--) {
current_x -= result->runs_[index]->width_;
AddRunInfoAdvances(*result->runs_[index], current_x, advances);
}
} else {
for (unsigned index = 0; index < run_count; index++) {
AddRunInfoAdvances(*result->runs_[index], current_x, advances);
current_x += result->runs_[index]->width_;
}
}
character_offset += result->NumCharacters();
}
return advances;
}
int ShapeResultBuffer::OffsetForPosition(
const TextRun& run,
float target_x,
IncludePartialGlyphsOption partial_glyphs,
BreakGlyphsOption break_glyphs) const {
StringView text = run.ToStringView();
unsigned total_offset;
if (run.Rtl()) {
total_offset = run.length();
for (unsigned i = results_.size(); i; --i) {
const scoped_refptr<const ShapeResult>& word_result = results_[i - 1];
if (!word_result)
continue;
total_offset -= word_result->NumCharacters();
if (target_x >= 0 && target_x <= word_result->Width()) {
int offset_for_word = word_result->OffsetForPosition(
target_x,
StringView(text, total_offset, word_result->NumCharacters()),
partial_glyphs, break_glyphs);
return total_offset + offset_for_word;
}
target_x -= word_result->Width();
}
} else {
total_offset = 0;
for (const scoped_refptr<const ShapeResult>& word_result : results_) {
if (!word_result)
continue;
int offset_for_word = word_result->OffsetForPosition(
target_x, StringView(text, 0, word_result->NumCharacters()),
partial_glyphs, break_glyphs);
DCHECK_GE(offset_for_word, 0);
total_offset += offset_for_word;
if (target_x >= 0 && target_x <= word_result->Width())
return total_offset;
text = StringView(text, word_result->NumCharacters());
target_x -= word_result->Width();
}
}
return total_offset;
}
void ShapeResultBuffer::ExpandRangeToIncludePartialGlyphs(int* from,
int* to) const {
int offset = 0;
for (unsigned j = 0; j < results_.size(); j++) {
const scoped_refptr<const ShapeResult> result = results_[j];
for (unsigned i = 0; i < result->runs_.size(); i++) {
if (!result->runs_[i])
continue;
result->runs_[i]->ExpandRangeToIncludePartialGlyphs(offset, from, to);
offset += result->runs_[i]->num_characters_;
}
}
}
Vector<ShapeResult::RunFontData> ShapeResultBuffer::GetRunFontData() const {
Vector<ShapeResult::RunFontData> font_data;
for (const auto& result : results_)
result->GetRunFontData(&font_data);
return font_data;
}
GlyphData ShapeResultBuffer::EmphasisMarkGlyphData(
const FontDescription& font_description) const {
for (const auto& result : results_) {
for (const auto& run : result->runs_) {
DCHECK(run->font_data_);
if (run->glyph_data_.IsEmpty())
continue;
return GlyphData(
run->glyph_data_[0].glyph,
run->font_data_->EmphasisMarkFontData(font_description).get(),
run->CanvasRotation());
}
}
return GlyphData();
}
} // namespace blink