Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(225)

Side by Side Diff: third_party/WebKit/Source/core/paint/InlineTextBoxPainter.cpp

Issue 2647923002: Support language-appropriate position for "text-underline-position:under" (Closed)
Patch Set: Resolved merge conflict Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 "core/paint/InlineTextBoxPainter.h" 5 #include "core/paint/InlineTextBoxPainter.h"
6 6
7 #include "core/editing/CompositionUnderline.h" 7 #include "core/editing/CompositionUnderline.h"
8 #include "core/editing/Editor.h" 8 #include "core/editing/Editor.h"
9 #include "core/editing/markers/DocumentMarkerController.h" 9 #include "core/editing/markers/DocumentMarkerController.h"
10 #include "core/frame/LocalFrame.h" 10 #include "core/frame/LocalFrame.h"
(...skipping 18 matching lines...) Expand all
29 const InlineTextBox& textBox) { 29 const InlineTextBox& textBox) {
30 const unsigned startOffset = marker.startOffset() > textBox.start() 30 const unsigned startOffset = marker.startOffset() > textBox.start()
31 ? marker.startOffset() - textBox.start() 31 ? marker.startOffset() - textBox.start()
32 : 0U; 32 : 0U;
33 const unsigned endOffset = 33 const unsigned endOffset =
34 std::min(marker.endOffset() - textBox.start(), textBox.len()); 34 std::min(marker.endOffset() - textBox.start(), textBox.len());
35 return std::make_pair(startOffset, endOffset); 35 return std::make_pair(startOffset, endOffset);
36 } 36 }
37 } 37 }
38 38
39 enum class ResolvedUnderlinePosition { Roman, Under };
40
41 static ResolvedUnderlinePosition resolveUnderlinePosition(
42 const ComputedStyle& style,
43 const InlineTextBox* inlineTextBox,
44 TextDecoration* flipDecoration) {
45 // |auto| should resolve to |under| to avoid drawing through glyphs in
46 // scripts where it would not be appropriate (e.g., ideographs.)
47 // However, this has performance implications. For now, we only work with
48 // vertical text.
49 switch (inlineTextBox->root().baselineType()) {
50 default:
51 NOTREACHED();
52 // Fall though.
53 case AlphabeticBaseline:
54 switch (style.getTextUnderlinePosition()) {
55 default:
56 NOTREACHED();
57 // Fall though.
58 case TextUnderlinePositionAuto:
59 return ResolvedUnderlinePosition::Roman;
60 case TextUnderlinePositionUnder:
61 return ResolvedUnderlinePosition::Under;
62 }
63 break;
64 case IdeographicBaseline:
65 // Compute language-appropriate default underline position.
66 // https://drafts.csswg.org/css-text-decor-3/#default-stylesheet
67 UScriptCode script = style.getFontDescription().script();
68 if (script == USCRIPT_KATAKANA_OR_HIRAGANA || script == USCRIPT_HANGUL)
drott 2017/02/07 16:20:52 Are we sure this couldn't be USCRIPT_KATAKANA or U
kojii 2017/02/07 23:13:44 The former, we use this convention in several plac
69 *flipDecoration = TextDecorationUnderline | TextDecorationOverline;
70 return ResolvedUnderlinePosition::Under;
71 }
72 }
73
39 static LineLayoutItem enclosingUnderlineObject( 74 static LineLayoutItem enclosingUnderlineObject(
40 const InlineTextBox* inlineTextBox) { 75 const InlineTextBox* inlineTextBox) {
41 bool firstLine = inlineTextBox->isFirstLineStyle(); 76 bool firstLine = inlineTextBox->isFirstLineStyle();
42 for (LineLayoutItem current = inlineTextBox->parent()->getLineLayoutItem(); 77 for (LineLayoutItem current = inlineTextBox->parent()->getLineLayoutItem();
43 ;) { 78 ;) {
44 if (current.isLayoutBlock()) 79 if (current.isLayoutBlock())
45 return current; 80 return current;
46 if (!current.isLayoutInline() || current.isRubyText()) 81 if (!current.isLayoutInline() || current.isRubyText())
47 return nullptr; 82 return nullptr;
48 83
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 126
92 if (fontMetrics.underlinePosition()) 127 if (fontMetrics.underlinePosition())
93 gap = -fontMetrics.underlinePosition(); 128 gap = -fontMetrics.underlinePosition();
94 else 129 else
95 gap = std::max<int>(1, ceilf(textDecorationThickness / 2.f)); 130 gap = std::max<int>(1, ceilf(textDecorationThickness / 2.f));
96 131
97 // Position underline near the alphabetic baseline. 132 // Position underline near the alphabetic baseline.
98 return fontMetrics.ascent() + gap; 133 return fontMetrics.ascent() + gap;
99 } 134 }
100 135
101 static int computeUnderlineOffset(const ComputedStyle& style, 136 static int computeUnderlineOffset(ResolvedUnderlinePosition underlinePosition,
137 const ComputedStyle& style,
102 const FontMetrics& fontMetrics, 138 const FontMetrics& fontMetrics,
103 const InlineTextBox* inlineTextBox, 139 const InlineTextBox* inlineTextBox,
104 const float textDecorationThickness) { 140 const float textDecorationThickness) {
105 // FIXME: We support only horizontal text for now. 141 switch (underlinePosition) {
106 switch (style.getTextUnderlinePosition()) {
107 default: 142 default:
108 NOTREACHED(); 143 NOTREACHED();
109 // Fall through. 144 // Fall through.
110 case TextUnderlinePositionAuto: 145 case ResolvedUnderlinePosition::Roman:
111 return computeUnderlineOffsetForRoman(fontMetrics, 146 return computeUnderlineOffsetForRoman(fontMetrics,
112 textDecorationThickness); 147 textDecorationThickness);
113 case TextUnderlinePositionUnder: { 148 case ResolvedUnderlinePosition::Under: {
114 // Position underline at the under edge of the lowest element's 149 // Position underline at the under edge of the lowest element's
115 // content box. 150 // content box.
116 LayoutUnit offset = computeUnderlineOffsetForUnder(style, inlineTextBox); 151 LayoutUnit offset = computeUnderlineOffsetForUnder(style, inlineTextBox);
117 offset = inlineTextBox->logicalHeight() + std::max(offset, LayoutUnit()); 152 offset = inlineTextBox->logicalHeight() + std::max(offset, LayoutUnit());
118 return offset.toInt(); 153 return offset.toInt();
119 } 154 }
120 } 155 }
121 } 156 }
122 157
158 static int computeOverlineOffset(const ComputedStyle& style,
159 const InlineTextBox* inlineTextBox) {
160 // Position underline at the over edge of the highest element's
161 // content box.
162 LayoutUnit offset = computeUnderlineOffsetForUnder(style, inlineTextBox);
drott 2017/02/07 16:20:52 I don't fully understand why this works and why th
kojii 2017/02/07 23:13:44 Maybe function name is bad...computeUnderlineOffse
163 return std::min(-offset, LayoutUnit()).toInt();
164 }
165
123 static bool shouldSetDecorationAntialias( 166 static bool shouldSetDecorationAntialias(
124 const Vector<AppliedTextDecoration>& decorations) { 167 const Vector<AppliedTextDecoration>& decorations) {
125 for (const AppliedTextDecoration& decoration : decorations) { 168 for (const AppliedTextDecoration& decoration : decorations) {
126 TextDecorationStyle decorationStyle = decoration.style(); 169 TextDecorationStyle decorationStyle = decoration.style();
127 if (decorationStyle == TextDecorationStyleDotted || 170 if (decorationStyle == TextDecorationStyleDotted ||
128 decorationStyle == TextDecorationStyleDashed) 171 decorationStyle == TextDecorationStyleDashed)
129 return true; 172 return true;
130 } 173 }
131 return false; 174 return false;
132 } 175 }
(...skipping 1000 matching lines...) Expand 10 before | Expand all | Expand 10 after
1133 1176
1134 context.setStrokeThickness(textDecorationThickness); 1177 context.setStrokeThickness(textDecorationThickness);
1135 1178
1136 bool antialiasDecoration = shouldSetDecorationAntialias(decorations); 1179 bool antialiasDecoration = shouldSetDecorationAntialias(decorations);
1137 1180
1138 // Offset between lines - always non-zero, so lines never cross each other. 1181 // Offset between lines - always non-zero, so lines never cross each other.
1139 float doubleOffset = textDecorationThickness + 1.f; 1182 float doubleOffset = textDecorationThickness + 1.f;
1140 bool skipIntercepts = 1183 bool skipIntercepts =
1141 styleToUse.getTextDecorationSkip() & TextDecorationSkipInk; 1184 styleToUse.getTextDecorationSkip() & TextDecorationSkipInk;
1142 1185
1186 // text-underline-position may flip underline and overline.
1187 TextDecoration flipDecoration = TextDecorationNone;
1188 ResolvedUnderlinePosition underlinePosition =
drott 2017/02/07 16:20:52 Minor and optional: I find this flipDecoration XOR
kojii 2017/02/07 23:13:44 Ok, will add if() on every loop. I think it's rare
1189 resolveUnderlinePosition(styleToUse, &m_inlineTextBox, &flipDecoration);
1190
1143 for (const AppliedTextDecoration& decoration : decorations) { 1191 for (const AppliedTextDecoration& decoration : decorations) {
1144 TextDecoration lines = decoration.lines(); 1192 TextDecoration lines = decoration.lines();
1193 lines = static_cast<TextDecoration>(lines ^ flipDecoration);
1145 if ((lines & TextDecorationUnderline) && fontData) { 1194 if ((lines & TextDecorationUnderline) && fontData) {
1146 const int underlineOffset = 1195 const int underlineOffset = computeUnderlineOffset(
1147 computeUnderlineOffset(styleToUse, fontData->getFontMetrics(), 1196 underlinePosition, styleToUse, fontData->getFontMetrics(),
1148 &m_inlineTextBox, textDecorationThickness); 1197 &m_inlineTextBox, textDecorationThickness);
1149 AppliedDecorationPainter decorationPainter( 1198 AppliedDecorationPainter decorationPainter(
1150 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset), 1199 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset),
1151 width.toFloat(), decoration, textDecorationThickness, doubleOffset, 1, 1200 width.toFloat(), decoration, textDecorationThickness, doubleOffset, 1,
1152 antialiasDecoration); 1201 antialiasDecoration);
1153 if (skipIntercepts) { 1202 if (skipIntercepts) {
1154 textPainter.clipDecorationsStripe( 1203 textPainter.clipDecorationsStripe(
1155 -baseline + decorationPainter.decorationBounds().y() - 1204 -baseline + decorationPainter.decorationBounds().y() -
1156 FloatPoint(localOrigin).y(), 1205 FloatPoint(localOrigin).y(),
1157 decorationPainter.decorationBounds().height(), 1206 decorationPainter.decorationBounds().height(),
1158 textDecorationThickness); 1207 textDecorationThickness);
1159 } 1208 }
1160 decorationPainter.paint(); 1209 decorationPainter.paint();
1161 } 1210 }
1162 if (lines & TextDecorationOverline) { 1211 if (lines & TextDecorationOverline) {
1212 const int overlineOffset =
1213 computeOverlineOffset(styleToUse, &m_inlineTextBox);
1163 AppliedDecorationPainter decorationPainter( 1214 AppliedDecorationPainter decorationPainter(
1164 context, FloatPoint(localOrigin), width.toFloat(), decoration, 1215 context, FloatPoint(localOrigin) + FloatPoint(0, overlineOffset),
1165 textDecorationThickness, -doubleOffset, 1, antialiasDecoration); 1216 width.toFloat(), decoration, textDecorationThickness, -doubleOffset,
1217 1, antialiasDecoration);
1166 if (skipIntercepts) { 1218 if (skipIntercepts) {
1167 textPainter.clipDecorationsStripe( 1219 textPainter.clipDecorationsStripe(
1168 -baseline + decorationPainter.decorationBounds().y() - 1220 -baseline + decorationPainter.decorationBounds().y() -
1169 FloatPoint(localOrigin).y(), 1221 FloatPoint(localOrigin).y(),
1170 decorationPainter.decorationBounds().height(), 1222 decorationPainter.decorationBounds().height(),
1171 textDecorationThickness); 1223 textDecorationThickness);
1172 } 1224 }
1173 decorationPainter.paint(); 1225 decorationPainter.paint();
1174 } 1226 }
1175 if (lines & TextDecorationLineThrough) { 1227 if (lines & TextDecorationLineThrough) {
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
1328 1380
1329 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(), 1381 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(),
1330 m_inlineTextBox.logicalHeight())); 1382 m_inlineTextBox.logicalHeight()));
1331 context.clip(FloatRect(boxRect)); 1383 context.clip(FloatRect(boxRect));
1332 context.drawHighlightForText(font, run, FloatPoint(boxOrigin), 1384 context.drawHighlightForText(font, run, FloatPoint(boxOrigin),
1333 boxRect.height().toInt(), color, 1385 boxRect.height().toInt(), color,
1334 paintOffsets.first, paintOffsets.second); 1386 paintOffsets.first, paintOffsets.second);
1335 } 1387 }
1336 1388
1337 } // namespace blink 1389 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698