OLD | NEW |
---|---|
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "platform/fonts/shaping/ShapingLineBreaker.h" | 5 #include "platform/fonts/shaping/ShapingLineBreaker.h" |
6 | 6 |
7 #include "platform/fonts/Font.h" | 7 #include "platform/fonts/Font.h" |
8 #include "platform/fonts/shaping/HarfBuzzShaper.h" | 8 #include "platform/fonts/shaping/HarfBuzzShaper.h" |
9 #include "platform/fonts/shaping/ShapeResult.h" | 9 #include "platform/fonts/shaping/ShapeResult.h" |
10 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" | 10 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" |
11 #include "platform/text/TextBreakIterator.h" | 11 #include "platform/text/TextBreakIterator.h" |
12 | 12 |
13 namespace blink { | 13 namespace blink { |
14 | 14 |
15 ShapingLineBreaker::ShapingLineBreaker(const HarfBuzzShaper* shaper, | 15 ShapingLineBreaker::ShapingLineBreaker( |
16 const Font* font, | 16 const HarfBuzzShaper* shaper, |
17 const ShapeResult* result, | 17 const Font* font, |
18 const AtomicString locale, | 18 const ShapeResult* result, |
19 LineBreakType break_type) | 19 const LazyLineBreakIterator* break_iterator) |
20 : shaper_(shaper), | 20 : shaper_(shaper), |
21 font_(font), | 21 font_(font), |
22 result_(result), | 22 result_(result), |
23 locale_(locale), | 23 break_iterator_(break_iterator) { |
24 break_type_(break_type) { | |
25 text_ = String(shaper->GetText(), shaper->TextLength()); | 24 text_ = String(shaper->GetText(), shaper->TextLength()); |
26 } | 25 } |
27 | 26 |
28 namespace { | 27 namespace { |
29 | 28 |
30 unsigned PreviousSafeToBreakAfter(const UChar* text, | 29 unsigned PreviousSafeToBreakAfter(const UChar* text, |
31 unsigned start, | 30 unsigned start, |
32 unsigned offset) { | 31 unsigned offset) { |
33 // TODO(eae): This is quite incorrect. It should be changed to use the | 32 // TODO(eae): This is quite incorrect. It should be changed to use the |
34 // HarfBuzzHarfBuzz safe to break info when available. | 33 // HarfBuzzHarfBuzz safe to break info when available. |
35 for (; offset > start; offset--) { | 34 for (; offset > start; offset--) { |
36 if (text[offset - 1] == kSpaceCharacter) | 35 if (text[offset - 1] == kSpaceCharacter) |
37 break; | 36 break; |
38 } | 37 } |
39 return offset; | 38 return offset; |
40 } | 39 } |
41 | 40 |
42 unsigned NextSafeToBreakBefore(const UChar* text, | 41 unsigned NextSafeToBreakBefore(const UChar* text, |
43 unsigned end, | 42 unsigned end, |
44 unsigned offset) { | 43 unsigned offset) { |
45 // TODO(eae): This is quite incorrect. It should be changed to use the | 44 // TODO(eae): This is quite incorrect. It should be changed to use the |
46 // HarfBuzzHarfBuzz safe to break info when available. | 45 // HarfBuzzHarfBuzz safe to break info when available. |
47 for (; offset < end; offset++) { | 46 for (; offset < end; offset++) { |
48 if (text[offset] == kSpaceCharacter) | 47 if (text[offset] == kSpaceCharacter) |
49 break; | 48 break; |
50 } | 49 } |
51 return offset; | 50 return offset; |
52 } | 51 } |
53 | 52 |
53 LayoutUnit FlipRtl(LayoutUnit value, TextDirection direction) { | |
54 return direction == TextDirection::kRtl ? -value : value; | |
55 } | |
56 | |
54 } // namespace | 57 } // namespace |
55 | 58 |
56 // Shapes a line of text by finding a valid and appropriate break opportunity | 59 // Shapes a line of text by finding a valid and appropriate break opportunity |
57 // based on the shaping results for the entire paragraph. Re-shapes the start | 60 // based on the shaping results for the entire paragraph. Re-shapes the start |
58 // and end of the line as needed. | 61 // and end of the line as needed. |
59 // | 62 // |
60 // Definitions: | 63 // Definitions: |
61 // Candidate break opportunity: Ideal point to break, disregarding line | 64 // Candidate break opportunity: Ideal point to break, disregarding line |
62 // breaking rules. May be in the middle of a word | 65 // breaking rules. May be in the middle of a word |
63 // or inside a ligature. | 66 // or inside a ligature. |
(...skipping 15 matching lines...) Expand all Loading... | |
79 // The candidate (or ideal) break opportunity would be at an offset of 10 as | 82 // The candidate (or ideal) break opportunity would be at an offset of 10 as |
80 // the break would happen at exactly 100px in that case. | 83 // the break would happen at exactly 100px in that case. |
81 // The previous valid break opportunity though is at an offset of 5. | 84 // The previous valid break opportunity though is at an offset of 5. |
82 // If we further assume that the font kerns with space then even though it's a | 85 // If we further assume that the font kerns with space then even though it's a |
83 // valid break opportunity reshaping is required as the combined width of the | 86 // valid break opportunity reshaping is required as the combined width of the |
84 // two segments "Line " and "breaking" may be different from "Line breaking". | 87 // two segments "Line " and "breaking" may be different from "Line breaking". |
85 PassRefPtr<ShapeResult> ShapingLineBreaker::ShapeLine( | 88 PassRefPtr<ShapeResult> ShapingLineBreaker::ShapeLine( |
86 unsigned start, | 89 unsigned start, |
87 LayoutUnit available_space, | 90 LayoutUnit available_space, |
88 unsigned* break_offset) { | 91 unsigned* break_offset) { |
92 unsigned range_start = result_->CharacterStartIndex(); | |
93 unsigned range_end = range_start + result_->NumCharacters(); | |
94 DCHECK_GE(start, range_start); | |
95 DCHECK_LT(start, range_end); | |
96 | |
89 // The start position in the original shape results. | 97 // The start position in the original shape results. |
90 LayoutUnit start_position = result_->SnappedStartPositionForOffset(start); | 98 float start_position_float = result_->PositionForOffset(start - range_start); |
99 LayoutUnit start_position = LayoutUnit::FromFloatFloor(start_position_float); | |
91 TextDirection direction = result_->Direction(); | 100 TextDirection direction = result_->Direction(); |
92 | 101 |
102 // Find a candidate break opportunity by identifying the last offset before | |
103 // exceeding the available space and the determine the closest valid break | |
104 // preceding the candidate. | |
105 LayoutUnit end_position = LayoutUnit::FromFloatCeil(start_position_float) + | |
106 FlipRtl(available_space, direction); | |
107 unsigned candidate_break = | |
108 result_->OffsetForPosition(end_position, | |
109 ShapeResult::kOutsideAsStartOrEnd) + | |
110 range_start; | |
111 DCHECK_GE(candidate_break, start); | |
112 unsigned break_opportunity = | |
113 break_iterator_->PreviousBreakOpportunity(candidate_break, start); | |
114 if (break_opportunity <= start) { | |
115 break_opportunity = break_iterator_->NextBreakOpportunity( | |
116 std::max(candidate_break, start + 1)); | |
117 } | |
118 DCHECK_GT(break_opportunity, start); | |
119 | |
93 // If the start offset is not at a safe-to-break boundary the content between | 120 // If the start offset is not at a safe-to-break boundary the content between |
94 // the start and the next safe-to-break boundary needs to be reshaped and the | 121 // the start and the next safe-to-break boundary needs to be reshaped and the |
95 // available space adjusted to take the reshaping into account. | 122 // available space adjusted to take the reshaping into account. |
96 RefPtr<ShapeResult> line_start_result; | 123 RefPtr<ShapeResult> line_start_result; |
97 unsigned first_safe = | 124 unsigned first_safe = |
98 NextSafeToBreakBefore(shaper_->GetText(), shaper_->TextLength(), start); | 125 NextSafeToBreakBefore(shaper_->GetText(), shaper_->TextLength(), start); |
99 if (first_safe != start) { | 126 DCHECK_GE(first_safe, start); |
127 // Reshape takes place only when first_safe is before the break opportunity. | |
eae
2017/05/12 18:45:49
I considered this case and wanted to fix it but fa
kojii
2017/05/12 19:08:13
Yes, the Han character in your ShapeLineArabicThai
| |
128 // Otherwise reshape will be part of line_end_result. | |
129 if (first_safe != start && first_safe < break_opportunity) { | |
100 LayoutUnit original_width = | 130 LayoutUnit original_width = |
101 result_->SnappedEndPositionForOffset(first_safe) - start_position; | 131 FlipRtl(result_->SnappedEndPositionForOffset(first_safe - range_start) - |
132 start_position, | |
133 direction); | |
102 line_start_result = shaper_->Shape(font_, direction, start, first_safe); | 134 line_start_result = shaper_->Shape(font_, direction, start, first_safe); |
103 available_space += line_start_result->SnappedWidth() - original_width; | 135 available_space += line_start_result->SnappedWidth() - original_width; |
104 } | 136 } |
105 | 137 |
106 // Find a candidate break opportunity by identifying the last offset before | |
107 // exceeding the available space and the determine the closest valid break | |
108 // preceding the candidate. | |
109 LazyLineBreakIterator break_iterator(text_, locale_, break_type_); | |
110 LayoutUnit end_position = start_position + available_space; | |
111 unsigned candidate_break = result_->OffsetForPosition(end_position, false); | |
112 unsigned break_opportunity = | |
113 break_iterator.PreviousBreakOpportunity(candidate_break, start); | |
114 if (break_opportunity <= start) { | |
115 break_opportunity = break_iterator.NextBreakOpportunity(candidate_break); | |
116 } | |
117 | |
118 RefPtr<ShapeResult> line_end_result; | 138 RefPtr<ShapeResult> line_end_result; |
119 unsigned last_safe = break_opportunity; | 139 unsigned last_safe = break_opportunity; |
120 while (break_opportunity > start) { | 140 while (break_opportunity > start) { |
121 // If the previous valid break opportunity is not at a safe-to-break | 141 // If the previous valid break opportunity is not at a safe-to-break |
122 // boundary reshape between the safe-to-break offset and the valid break | 142 // boundary reshape between the safe-to-break offset and the valid break |
123 // offset. If the resulting width exceeds the available space the | 143 // offset. If the resulting width exceeds the available space the |
124 // preceding boundary is tried until the available space is sufficient. | 144 // preceding boundary is tried until the available space is sufficient. |
125 unsigned previous_safe = std::max( | 145 unsigned previous_safe = std::max( |
126 PreviousSafeToBreakAfter(shaper_->GetText(), start, break_opportunity), | 146 PreviousSafeToBreakAfter(shaper_->GetText(), start, break_opportunity), |
127 start); | 147 start); |
148 DCHECK_LE(previous_safe, break_opportunity); | |
128 if (previous_safe != break_opportunity) { | 149 if (previous_safe != break_opportunity) { |
129 LayoutUnit safe_position = | 150 LayoutUnit safe_position = |
130 result_->SnappedStartPositionForOffset(previous_safe); | 151 result_->SnappedStartPositionForOffset(previous_safe - range_start); |
131 while (break_opportunity > previous_safe && previous_safe > start) { | 152 while (break_opportunity > previous_safe && previous_safe >= start) { |
132 line_end_result = | 153 line_end_result = |
133 shaper_->Shape(font_, direction, previous_safe, break_opportunity); | 154 shaper_->Shape(font_, direction, previous_safe, |
134 if (safe_position + line_end_result->SnappedWidth() <= end_position) | 155 std::min(break_opportunity, range_end)); |
156 if (line_end_result->SnappedWidth() <= | |
157 FlipRtl(end_position - safe_position, direction)) | |
135 break; | 158 break; |
159 // Doesn't fit after the reshape. Try previous break opportunity, or | |
160 // overflow if there were none. | |
161 unsigned previous_break_opportunity = | |
162 break_iterator_->PreviousBreakOpportunity(break_opportunity - 1, | |
163 start); | |
164 if (previous_break_opportunity <= start) | |
165 break; | |
166 break_opportunity = previous_break_opportunity; | |
136 line_end_result = nullptr; | 167 line_end_result = nullptr; |
137 break_opportunity = break_iterator.PreviousBreakOpportunity( | |
138 break_opportunity - 1, start); | |
139 } | 168 } |
140 } | 169 } |
141 | 170 |
142 if (break_opportunity > start) { | 171 if (break_opportunity > start) { |
143 last_safe = previous_safe; | 172 last_safe = previous_safe; |
144 break; | 173 break; |
145 } | 174 } |
146 | 175 |
147 // No suitable break opportunity, not exceeding the available space, | 176 // No suitable break opportunity, not exceeding the available space, |
148 // found. Choose the next valid one even though it will overflow. | 177 // found. Choose the next valid one even though it will overflow. |
149 break_opportunity = break_iterator.NextBreakOpportunity(candidate_break); | 178 break_opportunity = break_iterator_->NextBreakOpportunity(candidate_break); |
150 } | 179 } |
151 | 180 |
152 // Create shape results for the line by copying from the re-shaped result (if | 181 // Create shape results for the line by copying from the re-shaped result (if |
153 // reshaping was needed) and the original shape results. | 182 // reshaping was needed) and the original shape results. |
154 RefPtr<ShapeResult> line_result = ShapeResult::Create(font_, 0, direction); | 183 RefPtr<ShapeResult> line_result = ShapeResult::Create(font_, 0, direction); |
155 unsigned max_length = std::numeric_limits<unsigned>::max(); | 184 unsigned max_length = std::numeric_limits<unsigned>::max(); |
156 if (line_start_result) | 185 if (line_start_result) |
157 line_start_result->CopyRange(0, max_length, line_result.Get()); | 186 line_start_result->CopyRange(0, max_length, line_result.Get()); |
158 if (last_safe > first_safe) | 187 if (last_safe > first_safe) |
159 result_->CopyRange(first_safe, last_safe, line_result.Get()); | 188 result_->CopyRange(first_safe, last_safe, line_result.Get()); |
160 if (line_end_result) | 189 if (line_end_result) |
161 line_end_result->CopyRange(last_safe, max_length, line_result.Get()); | 190 line_end_result->CopyRange(last_safe, max_length, line_result.Get()); |
162 | 191 |
192 DCHECK_GT(break_opportunity, start); | |
193 DCHECK_EQ(std::min(break_opportunity, range_end) - start, | |
194 line_result->NumCharacters()); | |
195 | |
163 *break_offset = break_opportunity; | 196 *break_offset = break_opportunity; |
164 return line_result.Release(); | 197 return line_result.Release(); |
165 } | 198 } |
166 | 199 |
167 } // namespace blink | 200 } // namespace blink |
OLD | NEW |