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

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: Re-add TestExpectations Created 3 years, 10 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
« no previous file with comments | « third_party/WebKit/LayoutTests/platform/win7/fast/text/decorations-with-text-combine-expected.png ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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, Over };
40
41 static ResolvedUnderlinePosition resolveUnderlinePosition(
42 const ComputedStyle& style,
43 const InlineTextBox* inlineTextBox) {
44 // |auto| should resolve to |under| to avoid drawing through glyphs in
45 // scripts where it would not be appropriate (e.g., ideographs.)
46 // However, this has performance implications. For now, we only work with
47 // vertical text.
48 switch (inlineTextBox->root().baselineType()) {
49 default:
50 NOTREACHED();
51 // Fall though.
52 case AlphabeticBaseline:
53 switch (style.getTextUnderlinePosition()) {
54 default:
55 NOTREACHED();
56 // Fall though.
57 case TextUnderlinePositionAuto:
58 return ResolvedUnderlinePosition::Roman;
59 case TextUnderlinePositionUnder:
60 return ResolvedUnderlinePosition::Under;
61 }
62 break;
63 case IdeographicBaseline:
64 // Compute language-appropriate default underline position.
65 // https://drafts.csswg.org/css-text-decor-3/#default-stylesheet
66 UScriptCode script = style.getFontDescription().script();
67 if (script == USCRIPT_KATAKANA_OR_HIRAGANA || script == USCRIPT_HANGUL)
68 return ResolvedUnderlinePosition::Over;
69 return ResolvedUnderlinePosition::Under;
70 }
71 }
72
39 static LineLayoutItem enclosingUnderlineObject( 73 static LineLayoutItem enclosingUnderlineObject(
40 const InlineTextBox* inlineTextBox) { 74 const InlineTextBox* inlineTextBox) {
41 bool firstLine = inlineTextBox->isFirstLineStyle(); 75 bool firstLine = inlineTextBox->isFirstLineStyle();
42 for (LineLayoutItem current = inlineTextBox->parent()->getLineLayoutItem(); 76 for (LineLayoutItem current = inlineTextBox->parent()->getLineLayoutItem();
43 ;) { 77 ;) {
44 if (current.isLayoutBlock()) 78 if (current.isLayoutBlock())
45 return current; 79 return current;
46 if (!current.isLayoutInline() || current.isRubyText()) 80 if (!current.isLayoutInline() || current.isRubyText())
47 return nullptr; 81 return nullptr;
48 82
49 const ComputedStyle& styleToUse = current.styleRef(firstLine); 83 const ComputedStyle& styleToUse = current.styleRef(firstLine);
50 if (styleToUse.getTextDecoration() & TextDecorationUnderline) 84 if (styleToUse.getTextDecoration() & TextDecorationUnderline)
51 return current; 85 return current;
52 86
53 current = current.parent(); 87 current = current.parent();
54 if (!current) 88 if (!current)
55 return current; 89 return current;
56 90
57 if (Node* node = current.node()) { 91 if (Node* node = current.node()) {
58 if (isHTMLAnchorElement(node) || node->hasTagName(HTMLNames::fontTag)) 92 if (isHTMLAnchorElement(node) || node->hasTagName(HTMLNames::fontTag))
59 return current; 93 return current;
60 } 94 }
61 } 95 }
62 } 96 }
63 97
64 static LayoutUnit computeUnderlineOffsetForUnder( 98 static int computeUnderlineOffsetForUnder(const ComputedStyle& style,
65 const ComputedStyle& style, 99 const InlineTextBox* inlineTextBox,
66 const InlineTextBox* inlineTextBox) { 100 bool isOverline = false) {
67 const RootInlineBox& root = inlineTextBox->root(); 101 const RootInlineBox& root = inlineTextBox->root();
68 LineLayoutItem decorationObject = enclosingUnderlineObject(inlineTextBox); 102 LineLayoutItem decorationObject = enclosingUnderlineObject(inlineTextBox);
103 LayoutUnit offset;
69 if (style.isFlippedLinesWritingMode()) { 104 if (style.isFlippedLinesWritingMode()) {
70 LayoutUnit position = inlineTextBox->logicalTop(); 105 LayoutUnit position = inlineTextBox->logicalTop();
71 return position - 106 offset =
72 root.minLogicalTopForUnderline(decorationObject, position); 107 position - root.minLogicalTopForUnderline(decorationObject, position);
73 } else { 108 } else {
74 LayoutUnit position = inlineTextBox->logicalBottom(); 109 LayoutUnit position = inlineTextBox->logicalBottom();
75 return root.maxLogicalBottomForUnderline(decorationObject, position) - 110 offset = root.maxLogicalBottomForUnderline(decorationObject, position) -
76 position; 111 position;
77 } 112 }
113 if (isOverline)
114 return std::min(-offset, LayoutUnit()).toInt();
115 return (inlineTextBox->logicalHeight() + std::max(offset, LayoutUnit()))
116 .toInt();
117 }
118
119 static int computeOverlineOffset(const ComputedStyle& style,
120 const InlineTextBox* inlineTextBox) {
121 return computeUnderlineOffsetForUnder(style, inlineTextBox, true);
78 } 122 }
79 123
80 static int computeUnderlineOffsetForRoman(const FontMetrics& fontMetrics, 124 static int computeUnderlineOffsetForRoman(const FontMetrics& fontMetrics,
81 const float textDecorationThickness) { 125 const float textDecorationThickness) {
82 // Compute the gap between the font and the underline. Use at least one 126 // Compute the gap between the font and the underline. Use at least one
83 // pixel gap, if underline is thick then use a bigger gap. 127 // pixel gap, if underline is thick then use a bigger gap.
84 int gap = 0; 128 int gap = 0;
85 129
86 // Underline position of zero means draw underline on Baseline Position, 130 // Underline position of zero means draw underline on Baseline Position,
87 // in Blink we need at least 1-pixel gap to adding following check. 131 // in Blink we need at least 1-pixel gap to adding following check.
88 // Positive underline Position means underline should be drawn above baseline 132 // Positive underline Position means underline should be drawn above baseline
89 // and negative value means drawing below baseline, negating the value as in 133 // and negative value means drawing below baseline, negating the value as in
90 // Blink downward Y-increases. 134 // Blink downward Y-increases.
91 135
92 if (fontMetrics.underlinePosition()) 136 if (fontMetrics.underlinePosition())
93 gap = -fontMetrics.underlinePosition(); 137 gap = -fontMetrics.underlinePosition();
94 else 138 else
95 gap = std::max<int>(1, ceilf(textDecorationThickness / 2.f)); 139 gap = std::max<int>(1, ceilf(textDecorationThickness / 2.f));
96 140
97 // Position underline near the alphabetic baseline. 141 // Position underline near the alphabetic baseline.
98 return fontMetrics.ascent() + gap; 142 return fontMetrics.ascent() + gap;
99 } 143 }
100 144
101 static int computeUnderlineOffset(const ComputedStyle& style, 145 static int computeUnderlineOffset(ResolvedUnderlinePosition underlinePosition,
146 const ComputedStyle& style,
102 const FontMetrics& fontMetrics, 147 const FontMetrics& fontMetrics,
103 const InlineTextBox* inlineTextBox, 148 const InlineTextBox* inlineTextBox,
104 const float textDecorationThickness) { 149 const float textDecorationThickness) {
105 // FIXME: We support only horizontal text for now. 150 switch (underlinePosition) {
106 switch (style.getTextUnderlinePosition()) {
107 default: 151 default:
108 NOTREACHED(); 152 NOTREACHED();
109 // Fall through. 153 // Fall through.
110 case TextUnderlinePositionAuto: 154 case ResolvedUnderlinePosition::Roman:
111 return computeUnderlineOffsetForRoman(fontMetrics, 155 return computeUnderlineOffsetForRoman(fontMetrics,
112 textDecorationThickness); 156 textDecorationThickness);
113 case TextUnderlinePositionUnder: { 157 case ResolvedUnderlinePosition::Under:
114 // Position underline at the under edge of the lowest element's 158 // Position underline at the under edge of the lowest element's
115 // content box. 159 // content box.
116 LayoutUnit offset = computeUnderlineOffsetForUnder(style, inlineTextBox); 160 return computeUnderlineOffsetForUnder(style, inlineTextBox);
117 offset = inlineTextBox->logicalHeight() + std::max(offset, LayoutUnit());
118 return offset.toInt();
119 }
120 } 161 }
121 } 162 }
122 163
123 static bool shouldSetDecorationAntialias( 164 static bool shouldSetDecorationAntialias(
124 const Vector<AppliedTextDecoration>& decorations) { 165 const Vector<AppliedTextDecoration>& decorations) {
125 for (const AppliedTextDecoration& decoration : decorations) { 166 for (const AppliedTextDecoration& decoration : decorations) {
126 TextDecorationStyle decorationStyle = decoration.style(); 167 TextDecorationStyle decorationStyle = decoration.style();
127 if (decorationStyle == TextDecorationStyleDotted || 168 if (decorationStyle == TextDecorationStyleDotted ||
128 decorationStyle == TextDecorationStyleDashed) 169 decorationStyle == TextDecorationStyleDashed)
129 return true; 170 return true;
(...skipping 1004 matching lines...) Expand 10 before | Expand all | Expand 10 after
1134 1175
1135 context.setStrokeThickness(textDecorationThickness); 1176 context.setStrokeThickness(textDecorationThickness);
1136 1177
1137 bool antialiasDecoration = shouldSetDecorationAntialias(decorations); 1178 bool antialiasDecoration = shouldSetDecorationAntialias(decorations);
1138 1179
1139 // Offset between lines - always non-zero, so lines never cross each other. 1180 // Offset between lines - always non-zero, so lines never cross each other.
1140 float doubleOffset = textDecorationThickness + 1.f; 1181 float doubleOffset = textDecorationThickness + 1.f;
1141 bool skipIntercepts = 1182 bool skipIntercepts =
1142 styleToUse.getTextDecorationSkip() & TextDecorationSkipInk; 1183 styleToUse.getTextDecorationSkip() & TextDecorationSkipInk;
1143 1184
1185 // text-underline-position may flip underline and overline.
1186 ResolvedUnderlinePosition underlinePosition =
1187 resolveUnderlinePosition(styleToUse, &m_inlineTextBox);
1188 bool flipUnderlineAndOverline = false;
1189 if (underlinePosition == ResolvedUnderlinePosition::Over) {
1190 flipUnderlineAndOverline = true;
1191 underlinePosition = ResolvedUnderlinePosition::Under;
1192 }
1193
1144 for (const AppliedTextDecoration& decoration : decorations) { 1194 for (const AppliedTextDecoration& decoration : decorations) {
1145 TextDecoration lines = decoration.lines(); 1195 TextDecoration lines = decoration.lines();
1196 if (flipUnderlineAndOverline) {
1197 lines = static_cast<TextDecoration>(
1198 lines ^ (TextDecorationUnderline | TextDecorationOverline));
1199 }
1146 if ((lines & TextDecorationUnderline) && fontData) { 1200 if ((lines & TextDecorationUnderline) && fontData) {
1147 const int underlineOffset = 1201 const int underlineOffset = computeUnderlineOffset(
1148 computeUnderlineOffset(styleToUse, fontData->getFontMetrics(), 1202 underlinePosition, styleToUse, fontData->getFontMetrics(),
1149 &m_inlineTextBox, textDecorationThickness); 1203 &m_inlineTextBox, textDecorationThickness);
1150 AppliedDecorationPainter decorationPainter( 1204 AppliedDecorationPainter decorationPainter(
1151 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset), 1205 context, FloatPoint(localOrigin) + FloatPoint(0, underlineOffset),
1152 width.toFloat(), decoration, textDecorationThickness, doubleOffset, 1, 1206 width.toFloat(), decoration, textDecorationThickness, doubleOffset, 1,
1153 antialiasDecoration); 1207 antialiasDecoration);
1154 if (skipIntercepts) { 1208 if (skipIntercepts) {
1155 textPainter.clipDecorationsStripe( 1209 textPainter.clipDecorationsStripe(
1156 -baseline + decorationPainter.decorationBounds().y() - 1210 -baseline + decorationPainter.decorationBounds().y() -
1157 FloatPoint(localOrigin).y(), 1211 FloatPoint(localOrigin).y(),
1158 decorationPainter.decorationBounds().height(), 1212 decorationPainter.decorationBounds().height(),
1159 textDecorationThickness); 1213 textDecorationThickness);
1160 } 1214 }
1161 decorationPainter.paint(); 1215 decorationPainter.paint();
1162 } 1216 }
1163 if (lines & TextDecorationOverline) { 1217 if (lines & TextDecorationOverline) {
1218 const int overlineOffset =
1219 computeOverlineOffset(styleToUse, &m_inlineTextBox);
1164 AppliedDecorationPainter decorationPainter( 1220 AppliedDecorationPainter decorationPainter(
1165 context, FloatPoint(localOrigin), width.toFloat(), decoration, 1221 context, FloatPoint(localOrigin) + FloatPoint(0, overlineOffset),
1166 textDecorationThickness, -doubleOffset, 1, antialiasDecoration); 1222 width.toFloat(), decoration, textDecorationThickness, -doubleOffset,
1223 1, antialiasDecoration);
1167 if (skipIntercepts) { 1224 if (skipIntercepts) {
1168 textPainter.clipDecorationsStripe( 1225 textPainter.clipDecorationsStripe(
1169 -baseline + decorationPainter.decorationBounds().y() - 1226 -baseline + decorationPainter.decorationBounds().y() -
1170 FloatPoint(localOrigin).y(), 1227 FloatPoint(localOrigin).y(),
1171 decorationPainter.decorationBounds().height(), 1228 decorationPainter.decorationBounds().height(),
1172 textDecorationThickness); 1229 textDecorationThickness);
1173 } 1230 }
1174 decorationPainter.paint(); 1231 decorationPainter.paint();
1175 } 1232 }
1176 if (lines & TextDecorationLineThrough) { 1233 if (lines & TextDecorationLineThrough) {
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
1329 1386
1330 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(), 1387 LayoutRect boxRect(boxOrigin, LayoutSize(m_inlineTextBox.logicalWidth(),
1331 m_inlineTextBox.logicalHeight())); 1388 m_inlineTextBox.logicalHeight()));
1332 context.clip(FloatRect(boxRect)); 1389 context.clip(FloatRect(boxRect));
1333 context.drawHighlightForText(font, run, FloatPoint(boxOrigin), 1390 context.drawHighlightForText(font, run, FloatPoint(boxOrigin),
1334 boxRect.height().toInt(), color, 1391 boxRect.height().toInt(), color,
1335 paintOffsets.first, paintOffsets.second); 1392 paintOffsets.first, paintOffsets.second);
1336 } 1393 }
1337 1394
1338 } // namespace blink 1395 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/LayoutTests/platform/win7/fast/text/decorations-with-text-combine-expected.png ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698