blob: 3feb5d8cf13fd4637fc5e2c68139711b06f5b113 [file] [log] [blame]
// Copyright 2015 The Cobalt Authors. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <vector>
#include "base/memory/ref_counted.h"
#include "cobalt/dom/font_list.h"
#include "cobalt/layout/base_direction.h"
#include "cobalt/layout/layout_unit.h"
#include "cobalt/layout/line_wrapping.h"
#include "cobalt/render_tree/font.h"
#include "third_party/icu/source/common/unicode/brkiter.h"
#include "third_party/icu/source/common/unicode/locid.h"
#include "third_party/icu/source/common/unicode/unistr.h"
namespace cobalt {
namespace layout {
// The paragraph contains all of the text for a single layout paragraph. It
// handles both bidi and line breaking analysis for the text contained within
// it, while also serving as a repository for the text itself. Text boxes
// do not internally store their text, but instead keep a reference to their
// text paragraph along with their start and end indices within that paragraph,
// which allows them to retrieve both their specific text and any accompanying
// textual analysis from the text paragraph.
// The text paragraph is initially open and mutable. When the paragraph is
// closed, it becomes immutable and the Unicode bidirectional algorithm is
// applied to its text ( The text runs
// generated from this analysis are later used to both split boxes and organize
// boxes within a line according to their bidi levels.
// During layout, the the text paragraph determines line breaking locations for
// its text at soft wrap opportunities
// (, according to the
// Unicode line breaking algorithm (,
// which can result in text boxes being split.
class Paragraph : public base::RefCounted<Paragraph> {
// 'normal': Lines may break only at allowed break points.
// 'break-word': An unbreakable 'word' may be broken at an an arbitrary
// point...
enum BreakPolicy {
enum CodePoint {
enum DirectionalFormatting {
// Setting the "dir" attribute corresponds to using directional isolates.
enum TextOrder {
enum TextTransform {
typedef std::vector<DirectionalFormatting> DirectionalFormattingStack;
Paragraph(const icu::Locale& locale, BaseDirection base_direction,
const DirectionalFormattingStack& directional_formatting_stack,
icu::BreakIterator* line_break_iterator,
icu::BreakIterator* character_break_iterator);
// Append the string and return the position where the string begins.
int32 AppendUtf8String(const std::string& utf8_string);
int32 AppendUtf8String(const std::string& utf8_string,
TextTransform text_transform);
int32 AppendCodePoint(CodePoint code_point);
// Using the specified break policy, finds the last break position that fits
// within the available width. In the case where overflow is allowed and no
// break position is found within the available width, the first overflowing
// break position is used instead.
// The parameter |break_width| indicates the width of the portion of the
// substring coming before |break_position|.
// Returns false if no usable break position was found.
bool FindBreakPosition(const scoped_refptr<dom::FontList>& used_font,
int32 start_position, int32 end_position,
LayoutUnit available_width,
bool should_collapse_trailing_white_space,
bool allow_overflow, BreakPolicy break_policy,
int32* break_position, LayoutUnit* break_width);
int32 GetNextBreakPosition(int32 position, BreakPolicy break_policy);
int32 GetPreviousBreakPosition(int32 position, BreakPolicy break_policy);
bool IsBreakPosition(int32 position, BreakPolicy break_policy);
std::string RetrieveUtf8SubString(int32 start_position,
int32 end_position) const;
std::string RetrieveUtf8SubString(int32 start_position, int32 end_position,
TextOrder text_order) const;
const base::char16* GetTextBuffer() const;
const icu::Locale& GetLocale() const;
BaseDirection base_direction() const { return base_direction_; }
// Return the direction of the top directional formatting in the paragraph's
// stack or the base direction if the stack is empty.
BaseDirection GetDirectionalFormattingStackDirection() const;
int GetBidiLevel(int32 position) const;
bool IsRTL(int32 position) const;
bool IsCollapsibleWhiteSpace(int32 position) const;
bool GetNextRunPosition(int32 position, int32* next_run_position) const;
int32 GetTextEndPosition() const;
// Return the full directional formatting stack for the paragraph. This allows
// newly created paragraphs to copy the directional state of a preceding
// paragraph.
const DirectionalFormattingStack& GetDirectionalFormattingStack() const;
// Close the paragraph so that it becomes immutable and generates the text
// runs.
void Close();
bool IsClosed() const;
static BreakPolicy GetBreakPolicyFromWrapOpportunityPolicy(
WrapOpportunityPolicy wrap_opportunity_policy,
bool does_style_allow_break_word);
struct BidiLevelRun {
BidiLevelRun(int32 start_position, int level)
: start_position_(start_position), level_(level) {}
int32 start_position_;
int level_;
typedef std::vector<BidiLevelRun> BidiLevelRuns;
icu::BreakIterator* GetBreakIterator(BreakPolicy break_policy);
// Iterate over text segments as determined by the break iterator's strategy
// from the starting position, adding the width of each segment and
// determining the last one that fits within the available width. In the case
// where |allow_overflow| is true and the first segment overflows the width,
// that first overflowing segment will be included. The parameter
// |break_width| indicates the width of the portion of the substring coming
// before |break_position|.
void FindIteratorBreakPosition(const scoped_refptr<dom::FontList>& used_font,
icu::BreakIterator* const break_iterator,
int32 start_position, int32 end_position,
LayoutUnit available_width,
bool should_collapse_trailing_white_space,
bool allow_overflow, int32* break_position,
LayoutUnit* break_width);
// Attempt to include the specified segment within the available width. If
// either the segment fits within the width or |allow_overflow| is true, then
// |break_position| and |break_width| are updated to include the segment.
// NOTE: When |should_collapse_trailing_white_space| is true, then trailing
// white space in the segment is not included when determining if the segment
// can fit within the available width.
// |allow_overflow| is always set to false after the first segment is added,
// ensuring that only the first segment can overflow the available width.
// Returns false if the specified segment exceeded the available width.
// However, as a result of overflow potentially being allowed, a return value
// of false does not guarantee that the segment was not included, but simply
// that no additional segments can be included.
bool TryIncludeSegmentWithinAvailableWidth(
const scoped_refptr<dom::FontList>& used_font, int32 start_position,
int32 end_position, LayoutUnit available_width,
bool should_collapse_trailing_white_space, bool* allow_overflow,
int32* break_position, LayoutUnit* break_width);
void GenerateBidiLevelRuns();
size_t GetRunIndex(int32 position) const;
// A processed text in which:
// - collapsible white space has been collapsed and trimmed for both ends;
// - segment breaks have been transformed;
// - letter case has been transformed.
icu::UnicodeString unicode_text_;
// The locale of the paragraph.
const icu::Locale& locale_;
// The base direction of the paragraph.
const BaseDirection base_direction_;
// The stack tracking all active directional formatting in the paragraph.
DirectionalFormattingStack directional_formatting_stack_;
// The line break iterator to use when splitting the text boxes derived from
// this text paragraph across multiple lines.
icu::BreakIterator* const line_break_iterator_;
icu::BreakIterator* const character_break_iterator_;
// Whether or not the paragraph is open and modifiable or closed and
// immutable.
bool is_closed_;
// All of the bidi level runs contained within the paragraph.
BidiLevelRuns level_runs_;
// |last_retrieved_run_index_| is tracked so that the next retrieval search
// begins from this index. The vase majority of run retrievals either retrieve
// the same index as the previous one, or retrieve a neighboring index.
mutable size_t last_retrieved_run_index_;
// Allow the reference counting system access to our destructor.
friend class base::RefCounted<Paragraph>;
} // namespace layout
} // namespace cobalt