| // 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 |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef COBALT_LAYOUT_PARAGRAPH_H_ |
| #define COBALT_LAYOUT_PARAGRAPH_H_ |
| |
| #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 (http://www.unicode.org/reports/tr9/). 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 |
| // (https://www.w3.org/TR/css-text-3/#soft-wrap-opportunity), according to the |
| // Unicode line breaking algorithm (http://www.unicode.org/reports/tr14/), |
| // which can result in text boxes being split. |
| class Paragraph : public base::RefCounted<Paragraph> { |
| public: |
| // 'normal': Lines may break only at allowed break points. |
| // 'break-word': An unbreakable 'word' may be broken at an an arbitrary |
| // point... |
| // https://www.w3.org/TR/css-text-3/#overflow-wrap |
| enum BreakPolicy { |
| kBreakPolicyNormal, |
| kBreakPolicyBreakWord, |
| }; |
| |
| enum CodePoint { |
| kLeftToRightEmbedCodePoint, |
| kLineFeedCodePoint, |
| kNoBreakSpaceCodePoint, |
| kObjectReplacementCharacterCodePoint, |
| kPopDirectionalFormattingCharacterCodePoint, |
| kRightToLeftEmbedCodePoint, |
| }; |
| |
| // http://unicode.org/reports/tr9/#Explicit_Directional_Embeddings |
| enum DirectionalEmbedding { |
| kLeftToRightDirectionalEmbedding, |
| kRightToLeftDirectionalEmbedding, |
| }; |
| |
| enum TextOrder { |
| kLogicalTextOrder, |
| kVisualTextOrder, |
| }; |
| |
| enum TextTransform { |
| kNoTextTransform, |
| kUppercaseTextTransform, |
| }; |
| |
| typedef std::vector<DirectionalEmbedding> DirectionalEmbeddingStack; |
| |
| Paragraph(const icu::Locale& locale, BaseDirection base_direction, |
| const DirectionalEmbeddingStack& directional_embedding_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 embedding in the paragraph's |
| // stack or the base direction if the stack is empty. |
| BaseDirection GetDirectionalEmbeddingStackDirection() 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 embedding stack for the paragraph. This allows |
| // newly created paragraphs to copy the directional state of a preceding |
| // paragraph. |
| const DirectionalEmbeddingStack& GetDirectionalEmbeddingStack() 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); |
| |
| private: |
| 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 embeddings in the paragraph. |
| // http://unicode.org/reports/tr9/#Explicit_Directional_Embeddings |
| DirectionalEmbeddingStack directional_embedding_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 |
| |
| #endif // COBALT_LAYOUT_PARAGRAPH_H_ |