chromium/ui/gfx/render_text_harfbuzz.cc

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gfx/render_text_harfbuzz.h"

#include <limits>
#include <set>
#include <string_view>

#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/lru_cache.h"
#include "base/containers/span.h"
#include "base/debug/alias.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/hash/hash.h"
#include "base/i18n/base_i18n_switches.h"
#include "base/i18n/break_iterator.h"
#include "base/i18n/char_iterator.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/current_thread.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "skia/ext/font_utils.h"
#include "third_party/icu/source/common/unicode/ubidi.h"
#include "third_party/icu/source/common/unicode/uscript.h"
#include "third_party/icu/source/common/unicode/utf16.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkFontMetrics.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "ui/gfx/bidi_line_iterator.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/decorated_text.h"
#include "ui/gfx/font.h"
#include "ui/gfx/font_fallback.h"
#include "ui/gfx/font_render_params.h"
#include "ui/gfx/harfbuzz_font_skia.h"
#include "ui/gfx/platform_font.h"
#include "ui/gfx/range/range_f.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/switches.h"
#include "ui/gfx/text_utils.h"
#include "ui/gfx/utf16_indexing.h"

#if BUILDFLAG(IS_APPLE)
#include "base/apple/foundation_util.h"
#include "base/mac/mac_util.h"
#include "third_party/skia/include/ports/SkTypeface_mac.h"
#endif

#if BUILDFLAG(IS_ANDROID)
#include "base/android/locale_utils.h"
#endif  // BUILDFLAG(IS_ANDROID)

#include <hb.h>

namespace gfx {

namespace {

// Text length limit. Longer strings are slow and not fully tested.
const size_t kMaxTextLength =;

// The maximum number of scripts a Unicode character can belong to. This value
// is arbitrarily chosen to be a good limit because it is unlikely for a single
// character to belong to more scripts.
const size_t kMaxScripts =;

// Font fallback mechanism used to Shape runs (see ShapeRuns(...)).
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class ShapeRunFallback {};

// Log the fallback font mechanism used for shaping to UMA (see ShapeRuns(...)).
void RecordShapeRunsFallback(ShapeRunFallback fallback) {}

// Returns whether the codepoint has the 'extended pictographic' property.
bool IsExtendedPictographicCodepoint(UChar32 codepoint) {}

// Returns whether the codepoint has emoji properties.
bool IsEmojiRelatedCodepoint(UChar32 codepoint) {}

// Returns true if |codepoint| is a bracket. This is used to avoid "matching"
// brackets picking different font fallbacks, thereby appearing mismatched.
bool IsBracket(UChar32 codepoint) {}

// Returns a vector containing the script and the script extensions of the
// Unicode |codepoint|.
std::vector<UScriptCode> GetScriptExtensions(UChar32 codepoint) {}

// Intersects the script extensions set of |codepoint| with |result| and writes
// to |result|.
void ScriptSetIntersect(UChar32 codepoint, std::vector<UScriptCode>& result) {}

struct GraphemeProperties {};

// Returns the properties for the codepoints part of the given text.
GraphemeProperties RetrieveGraphemeProperties(std::u16string_view text,
                                              bool retrieve_block) {}

// Return whether the grapheme properties are compatible and the grapheme can
// be merge together in the same grapheme cluster.
bool AreGraphemePropertiesCompatible(const GraphemeProperties& first,
                                     const GraphemeProperties& second) {}

// Returns the end of the current grapheme cluster. This function is finding the
// breaking point where grapheme properties are no longer compatible
// (see: UNICODE TEXT SEGMENTATION (http://unicode.org/reports/tr29/).
// Breaks between |run_start| and |run_end| and force break after the grapheme
// starting at |run_break|.
size_t FindRunBreakingCharacter(const std::u16string& text,
                                UScriptCode script,
                                size_t run_start,
                                size_t run_break,
                                size_t run_end) {}

// Find the longest sequence of characters from 0 and up to |length| that have
// at least one common UScriptCode value. Writes the common script value to
// |script| and returns the length of the sequence. Takes the characters' script
// extensions into account. http://www.unicode.org/reports/tr24/#ScriptX
//
// Consider 3 characters with the script values {Kana}, {Hira, Kana}, {Kana}.
// Without script extensions only the first script in each set would be taken
// into account, resulting in 3 runs where 1 would be enough.
size_t ScriptInterval(const std::u16string& text,
                      size_t start,
                      size_t length,
                      UScriptCode* script) {}

// A port of hb_icu_script_to_script because harfbuzz on CrOS is built without
// hb-icu. See http://crbug.com/356929
inline hb_script_t ICUScriptToHBScript(UScriptCode script) {}

bool FontWasAlreadyTried(sk_sp<SkTypeface> typeface,
                         std::set<SkTypefaceID>* fallback_fonts) {}

void MarkFontAsTried(sk_sp<SkTypeface> typeface,
                     std::set<SkTypefaceID>* fallback_fonts) {}

// Whether |segment| corresponds to the newline character.
bool IsNewlineSegment(const std::u16string& text,
                      const internal::LineSegment& segment) {}

// Returns the line index considering the newline character. Line index is
// incremented if the caret is right after the newline character, i.e, the
// cursor affinity is |CURSOR_BACKWARD| while containing the newline character.
size_t LineIndexForNewline(const size_t line_index,
                           const std::u16string& text,
                           const internal::LineSegment& segment,
                           const SelectionModel& caret) {}

// Helper template function for |TextRunHarfBuzz::GetClusterAt()|. |Iterator|
// can be a forward or reverse iterator type depending on the text direction.
// Returns true on success, or false if an error is encountered.
template <class Iterator>
bool GetClusterAtImpl(size_t pos,
                      Range range,
                      Iterator elements_begin,
                      Iterator elements_end,
                      bool reversed,
                      Range* chars,
                      Range* glyphs) {}

// Internal class to generate Line structures. If |multiline| is true, the text
// is broken into lines at |words| boundaries such that each line is no longer
// than |max_width|. If |multiline| is false, only outputs a single Line from
// the given runs. |min_baseline| and |min_height| are the minimum baseline and
// height for each line.
// TODO(ckocagil): Expose the interface of this class in the header and test
//                 this class directly.
class HarfBuzzLineBreaker {};

// Applies a forced text rendering direction if specified by a command-line
// switch.
void ApplyForcedDirection(UBiDiLevel* level) {}

internal::TextRunHarfBuzz::FontParams CreateFontParams(
    const Font& primary_font,
    UBiDiLevel bidi_level,
    UScriptCode script,
    const internal::StyleIterator& style) {}

BASE_FEATURE();

bool IsRemoveFontLinkFallbacks() {}

BASE_FEATURE();

bool IsEnableFallbackFontsCrashReporting() {}

// Append to `in_out_report` the font name and the text correlating to the runs
// shaped by that font. This crash report will be used to debug why text is
// being shaped through the GetFallbackFonts path as we shouldn't need to
// fallback to that call path. crbug.com/995789
void AppendFontNameAndShapedTextToCrashDumpReport(
    const std::u16string& text,
    const std::vector<internal::TextRunHarfBuzz*>& shaped_runs,
    const std::string& font_name,
    std::u16string& report) {}

}  // namespace

namespace internal {

sk_sp<SkTypeface> CreateSkiaTypeface(const Font& font,
                                     bool italic,
                                     Font::Weight weight) {}

TextRunHarfBuzz::FontParams::FontParams(const Font& template_font)
    :{}
TextRunHarfBuzz::FontParams::~FontParams() = default;
TextRunHarfBuzz::FontParams::FontParams(
    const TextRunHarfBuzz::FontParams& other) = default;
TextRunHarfBuzz::FontParams& TextRunHarfBuzz::FontParams::operator=(
    const TextRunHarfBuzz::FontParams& other) = default;

bool TextRunHarfBuzz::FontParams::operator==(const FontParams& other) const {}

void TextRunHarfBuzz::FontParams::
    ComputeRenderParamsFontSizeAndBaselineOffset() {}

size_t TextRunHarfBuzz::FontParams::Hash::operator()(
    const FontParams& key) const {}

bool TextRunHarfBuzz::FontParams::SetRenderParamsRematchFont(
    const Font& new_font,
    const FontRenderParams& new_render_params) {}

bool TextRunHarfBuzz::FontParams::SetRenderParamsOverrideSkiaFaceFromFont(
    const Font& fallback_font,
    const FontRenderParams& new_render_params) {}

TextRunHarfBuzz::ShapeOutput::ShapeOutput() = default;
TextRunHarfBuzz::ShapeOutput::~ShapeOutput() = default;
TextRunHarfBuzz::ShapeOutput::ShapeOutput(
    const TextRunHarfBuzz::ShapeOutput& other) = default;
TextRunHarfBuzz::ShapeOutput& TextRunHarfBuzz::ShapeOutput::operator=(
    const TextRunHarfBuzz::ShapeOutput& other) = default;
TextRunHarfBuzz::ShapeOutput::ShapeOutput(
    TextRunHarfBuzz::ShapeOutput&& other) = default;
TextRunHarfBuzz::ShapeOutput& TextRunHarfBuzz::ShapeOutput::operator=(
    TextRunHarfBuzz::ShapeOutput&& other) = default;

TextRunHarfBuzz::TextRunHarfBuzz(const Font& template_font)
    :{}

TextRunHarfBuzz::~TextRunHarfBuzz() {}

Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const {}

size_t TextRunHarfBuzz::CountMissingGlyphs() const {}

void TextRunHarfBuzz::GetClusterAt(size_t pos,
                                   Range* chars,
                                   Range* glyphs) const {}

RangeF TextRunHarfBuzz::GetGraphemeBounds(RenderTextHarfBuzz* render_text,
                                          size_t text_index) const {}

RangeF TextRunHarfBuzz::GetGraphemeSpanForCharRange(
    RenderTextHarfBuzz* render_text,
    const Range& char_range) const {}

SkScalar TextRunHarfBuzz::GetGlyphWidthForCharRange(
    const Range& char_range) const {}

void TextRunHarfBuzz::UpdateFontParamsAndShape(
    const FontParams& new_font_params,
    const ShapeOutput& new_shape) {}

TextRunList::TextRunList() :{}

TextRunList::~TextRunList() {}

void TextRunList::Reset() {}

void TextRunList::InitIndexMap() {}

void TextRunList::ComputePrecedingRunWidths() {}

size_t TextRunList::GetRunIndexAt(size_t position) const {}

namespace {

// ShapeRunWithFont cache. Views makes repeated calls to ShapeRunWithFont
// with the same arguments in several places, and typesetting is very expensive.
// To compensate for this, encapsulate all of the input arguments to
// ShapeRunWithFont in ShapeRunWithFontInput, all of the output arguments in
// TextRunHarfBuzz::ShapeOutput, and add ShapeRunCache to map between the two.
// This is analogous to the blink::ShapeCache.
// https://crbug.com/826265

// Input for the stateless implementation of ShapeRunWithFont.
struct ShapeRunWithFontInput {};

// An LRU cache of the results from calling ShapeRunWithFont. The maximum cache
// size used in blink::ShapeCache is 10k. A Finch experiment showed that
// reducing the cache size to 1k has no performance impact.
constexpr int kShapeRunCacheSize =;
ShapeRunCacheBase;
class ShapeRunCache : public ShapeRunCacheBase {};

void ShapeRunWithFont(const ShapeRunWithFontInput& in,
                      TextRunHarfBuzz::ShapeOutput* out) {}

std::string GetApplicationLocale() {}

}  // namespace

}  // namespace internal

RenderTextHarfBuzz::RenderTextHarfBuzz()
    :{}

RenderTextHarfBuzz::~RenderTextHarfBuzz() {}

const std::u16string& RenderTextHarfBuzz::GetDisplayText() {}

SizeF RenderTextHarfBuzz::GetStringSizeF() {}

SizeF RenderTextHarfBuzz::GetLineSizeF(const SelectionModel& caret) {}

std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) {}

RangeF RenderTextHarfBuzz::GetCursorSpan(const Range& text_range) {}

size_t RenderTextHarfBuzz::GetLineContainingCaret(const SelectionModel& caret) {}

SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel(
    const SelectionModel& selection,
    VisualCursorDirection direction) {}

SelectionModel RenderTextHarfBuzz::AdjacentWordSelectionModel(
    const SelectionModel& selection,
    VisualCursorDirection direction) {}

SelectionModel RenderTextHarfBuzz::AdjacentLineSelectionModel(
    const SelectionModel& selection,
    VisualCursorDirection direction) {}

void RenderTextHarfBuzz::OnLayoutTextAttributeChanged() {}

void RenderTextHarfBuzz::OnDisplayTextAttributeChanged() {}

void RenderTextHarfBuzz::EnsureLayout() {}

void RenderTextHarfBuzz::DrawVisualText(internal::SkiaTextRenderer* renderer,
                                        const std::vector<Range>& selections) {}

size_t RenderTextHarfBuzz::GetRunContainingCaret(
    const SelectionModel& caret) {}

SelectionModel RenderTextHarfBuzz::FirstSelectionModelInsideRun(
    const internal::TextRunHarfBuzz* run) {}

SelectionModel RenderTextHarfBuzz::LastSelectionModelInsideRun(
    const internal::TextRunHarfBuzz* run) {}

void RenderTextHarfBuzz::BuildResolvedTypefaceBreakList(
    internal::TextRunList* run_list) {}

void RenderTextHarfBuzz::ItemizeAndShapeText(const std::u16string& text,
                                             internal::TextRunList* run_list) {}

bool RenderTextHarfBuzz::ItemizeAndShapeTextImpl(
    CommonizedRunsMap* commonized_run_map,
    const std::u16string& text,
    internal::TextRunList* run_list) {}

void RenderTextHarfBuzz::ItemizeTextToRuns(
    const std::u16string& text,
    internal::TextRunList* out_run_list,
    CommonizedRunsMap* out_commonized_run_map) {}

bool RenderTextHarfBuzz::ShapeRuns(
    const std::u16string& text,
    const internal::TextRunHarfBuzz::FontParams& font_params,
    std::vector<internal::TextRunHarfBuzz*> runs) {}

void RenderTextHarfBuzz::ShapeRunsWithFont(
    const std::u16string& text,
    const internal::TextRunHarfBuzz::FontParams& font_params,
    std::vector<internal::TextRunHarfBuzz*>* in_out_runs,
    std::vector<internal::TextRunHarfBuzz*>* successfully_shaped_runs) {}

void RenderTextHarfBuzz::EnsureLayoutRunList() {}

// Returns the current run list, |display_run_list_| if the text is elided, or
// |layout_run_list_| otherwise.
internal::TextRunList* RenderTextHarfBuzz::GetRunList() {}

const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const {}

bool RenderTextHarfBuzz::IsValidDisplayRange(Range display_range) {}

void RenderTextHarfBuzz::GetDecoratedTextForRange(
    const Range& text_range,
    DecoratedText* decorated_text) {}

}  // namespace gfx