e86944cbf4a19c3332f9e10427396f7516188905
[WebKit-https.git] / Source / WebCore / style / InlineTextBoxStyle.cpp
1 /*
2  * Copyright (C) 2014 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "InlineTextBoxStyle.h"
28
29 #include "FontCascade.h"
30 #include "InlineTextBox.h"
31 #include "RootInlineBox.h"
32
33 namespace WebCore {
34     
35 int computeUnderlineOffset(TextUnderlinePosition underlinePosition, TextUnderlineOffset underlineOffset, const FontMetrics& fontMetrics, const InlineTextBox* inlineTextBox, int defaultGap)
36 {
37     // This represents the gap between the baseline and the closest edge of the underline.
38     int gap = std::max<int>(1, ceilf(defaultGap / 2.0));
39     
40     // FIXME: The code for visual overflow detection passes in a null inline text box. This means it is now
41     // broken for the case where auto needs to behave like "under".
42     
43     // According to the specification TextUnderlinePosition::Auto should avoid drawing through glyphs in
44     // scripts where it would not be appropriate (e.g., ideographs).
45     // Strictly speaking this can occur whenever the line contains ideographs
46     // even if it is horizontal, but detecting this has performance implications. For now we only work with
47     // vertical text, since we already determined the baseline type to be ideographic in that
48     // case.
49     
50     auto resolvedUnderlinePosition = underlinePosition;
51     if (resolvedUnderlinePosition == TextUnderlinePosition::Auto && underlineOffset.isAuto()) {
52         if (inlineTextBox)
53             resolvedUnderlinePosition = inlineTextBox->root().baselineType() == IdeographicBaseline ? TextUnderlinePosition::Under : TextUnderlinePosition::Auto;
54         else
55             resolvedUnderlinePosition = TextUnderlinePosition::Auto;
56     }
57     
58     switch (resolvedUnderlinePosition) {
59     case TextUnderlinePosition::Auto:
60         if (underlineOffset.isAuto())
61             return fontMetrics.ascent() + gap;
62         return fontMetrics.ascent() + std::max(0.0f, underlineOffset.lengthValue());
63     case TextUnderlinePosition::FromFont:
64         return fontMetrics.ascent() + std::max(0.0f, fontMetrics.underlinePosition() + underlineOffset.lengthOr(0));
65     case TextUnderlinePosition::Under: {
66         ASSERT(inlineTextBox);
67         // Position underline relative to the bottom edge of the lowest element's content box.
68         const RootInlineBox& rootBox = inlineTextBox->root();
69         const RenderElement* decorationRenderer = inlineTextBox->parent()->renderer().enclosingRendererWithTextDecoration(TextDecoration::Underline, inlineTextBox->isFirstLine());
70         
71         float offset;
72         if (inlineTextBox->renderer().style().isFlippedLinesWritingMode()) {
73             offset = inlineTextBox->logicalTop();
74             rootBox.minLogicalTopForTextDecorationLine(offset, decorationRenderer, TextDecoration::Underline);
75             offset = inlineTextBox->logicalTop() - offset;
76         } else {
77             offset = inlineTextBox->logicalBottom();
78             rootBox.maxLogicalBottomForTextDecorationLine(offset, decorationRenderer, TextDecoration::Underline);
79             offset -= inlineTextBox->logicalBottom();
80         }
81         auto desiredOffset = inlineTextBox->logicalHeight() + gap + std::max<float>(offset, 0) + underlineOffset.lengthOr(0);
82         desiredOffset = std::max<float>(desiredOffset, fontMetrics.ascent());
83         return desiredOffset;
84     }
85     }
86
87     ASSERT_NOT_REACHED();
88     return fontMetrics.ascent() + gap;
89 }
90     
91 WavyStrokeParameters getWavyStrokeParameters(float fontSize)
92 {
93     WavyStrokeParameters result;
94     // More information is in the WavyStrokeParameters definition.
95     result.controlPointDistance = fontSize * 1.5 / 16;
96     result.step = fontSize / 4.5;
97     return result;
98 }
99
100 static inline void extendIntToFloat(int& extendMe, float extendTo)
101 {
102     extendMe = std::max(extendMe, static_cast<int>(ceilf(extendTo)));
103 }
104
105 GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, const InlineTextBox* inlineTextBox)
106 {
107     ASSERT(!inlineTextBox || inlineTextBox->lineStyle() == lineStyle);
108     
109     auto decoration = lineStyle.textDecorationsInEffect();
110     if (decoration.isEmpty())
111         return GlyphOverflow();
112
113     float strokeThickness = lineStyle.textDecorationThickness().resolve(lineStyle.computedFontSize(), lineStyle.fontMetrics());
114     WavyStrokeParameters wavyStrokeParameters;
115     float wavyOffset = 0;
116         
117     TextDecorationStyle decorationStyle = lineStyle.textDecorationStyle();
118     float height = lineStyle.fontCascade().fontMetrics().floatHeight();
119     GlyphOverflow overflowResult;
120     
121     if (decorationStyle == TextDecorationStyle::Wavy) {
122         wavyStrokeParameters = getWavyStrokeParameters(lineStyle.computedFontPixelSize());
123         wavyOffset = wavyOffsetFromDecoration();
124         overflowResult.left = strokeThickness;
125         overflowResult.right = strokeThickness;
126     }
127
128     // These metrics must match where underlines get drawn.
129     if (decoration & TextDecoration::Underline) {
130         // Compensate for the integral ceiling in GraphicsContext::computeLineBoundsAndAntialiasingModeForText()
131         int underlineOffset = 1;
132         float textDecorationBaseFontSize = 16;
133         auto defaultGap = lineStyle.computedFontSize() / textDecorationBaseFontSize;
134         underlineOffset += computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.textUnderlineOffset(), lineStyle.fontMetrics(), inlineTextBox, defaultGap);
135         if (decorationStyle == TextDecorationStyle::Wavy) {
136             extendIntToFloat(overflowResult.bottom, underlineOffset + wavyOffset + wavyStrokeParameters.controlPointDistance + strokeThickness - height);
137             extendIntToFloat(overflowResult.top, -(underlineOffset + wavyOffset - wavyStrokeParameters.controlPointDistance - strokeThickness));
138         } else {
139             extendIntToFloat(overflowResult.bottom, underlineOffset + strokeThickness - height);
140             extendIntToFloat(overflowResult.top, -underlineOffset);
141         }
142     }
143     if (decoration & TextDecoration::Overline) {
144         if (decorationStyle == TextDecorationStyle::Wavy) {
145             extendIntToFloat(overflowResult.bottom, -wavyOffset + wavyStrokeParameters.controlPointDistance + strokeThickness - height);
146             extendIntToFloat(overflowResult.top, wavyOffset + wavyStrokeParameters.controlPointDistance + strokeThickness);
147         } else {
148             extendIntToFloat(overflowResult.bottom, strokeThickness - height);
149             // top is untouched
150         }
151     }
152     if (decoration & TextDecoration::LineThrough) {
153         float baseline = lineStyle.fontMetrics().floatAscent();
154         if (decorationStyle == TextDecorationStyle::Wavy) {
155             extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + wavyStrokeParameters.controlPointDistance + strokeThickness - height);
156             extendIntToFloat(overflowResult.top, -(2 * baseline / 3 - wavyStrokeParameters.controlPointDistance - strokeThickness));
157         } else {
158             extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + strokeThickness - height);
159             extendIntToFloat(overflowResult.top, -(2 * baseline / 3));
160         }
161     }
162     return overflowResult;
163 }
164     
165 }