Address post-review comments after r237955
[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 float computeUnderlineOffset(TextUnderlinePosition underlinePosition, TextUnderlineOffset underlineOffset, const FontMetrics& fontMetrics, const InlineTextBox* inlineTextBox, float defaultGap)
36 {
37     // This represents the gap between the baseline and the closest edge of the underline.
38     float gap = std::max<int>(1, std::ceil(defaultGap / 2.0f));
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(offset, 0.0f) + underlineOffset.lengthOr(0);
82         return std::max<float>(desiredOffset, fontMetrics.ascent());
83     }
84     }
85
86     ASSERT_NOT_REACHED();
87     return fontMetrics.ascent() + gap;
88 }
89     
90 WavyStrokeParameters getWavyStrokeParameters(float fontSize)
91 {
92     WavyStrokeParameters result;
93     // More information is in the WavyStrokeParameters definition.
94     result.controlPointDistance = fontSize * 1.5 / 16;
95     result.step = fontSize / 4.5;
96     return result;
97 }
98
99 static inline void extendIntToFloat(int& extendMe, float extendTo)
100 {
101     extendMe = std::max(extendMe, static_cast<int>(ceilf(extendTo)));
102 }
103
104 GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, const InlineTextBox* inlineTextBox)
105 {
106     ASSERT(!inlineTextBox || inlineTextBox->lineStyle() == lineStyle);
107     
108     auto decoration = lineStyle.textDecorationsInEffect();
109     if (decoration.isEmpty())
110         return GlyphOverflow();
111
112     float strokeThickness = lineStyle.textDecorationThickness().resolve(lineStyle.computedFontSize(), lineStyle.fontMetrics());
113     WavyStrokeParameters wavyStrokeParameters;
114     float wavyOffset = 0;
115         
116     TextDecorationStyle decorationStyle = lineStyle.textDecorationStyle();
117     float height = lineStyle.fontCascade().fontMetrics().floatHeight();
118     GlyphOverflow overflowResult;
119     
120     if (decorationStyle == TextDecorationStyle::Wavy) {
121         wavyStrokeParameters = getWavyStrokeParameters(lineStyle.computedFontPixelSize());
122         wavyOffset = wavyOffsetFromDecoration();
123         overflowResult.left = strokeThickness;
124         overflowResult.right = strokeThickness;
125     }
126
127     // These metrics must match where underlines get drawn.
128     if (decoration & TextDecoration::Underline) {
129         // Compensate for the integral ceiling in GraphicsContext::computeLineBoundsAndAntialiasingModeForText()
130         int underlineOffset = 1;
131         float textDecorationBaseFontSize = 16;
132         auto defaultGap = lineStyle.computedFontSize() / textDecorationBaseFontSize;
133         underlineOffset += computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.textUnderlineOffset(), lineStyle.fontMetrics(), inlineTextBox, defaultGap);
134         if (decorationStyle == TextDecorationStyle::Wavy) {
135             extendIntToFloat(overflowResult.bottom, underlineOffset + wavyOffset + wavyStrokeParameters.controlPointDistance + strokeThickness - height);
136             extendIntToFloat(overflowResult.top, -(underlineOffset + wavyOffset - wavyStrokeParameters.controlPointDistance - strokeThickness));
137         } else {
138             extendIntToFloat(overflowResult.bottom, underlineOffset + strokeThickness - height);
139             extendIntToFloat(overflowResult.top, -underlineOffset);
140         }
141     }
142     if (decoration & TextDecoration::Overline) {
143         if (decorationStyle == TextDecorationStyle::Wavy) {
144             extendIntToFloat(overflowResult.bottom, -wavyOffset + wavyStrokeParameters.controlPointDistance + strokeThickness - height);
145             extendIntToFloat(overflowResult.top, wavyOffset + wavyStrokeParameters.controlPointDistance + strokeThickness);
146         } else {
147             extendIntToFloat(overflowResult.bottom, strokeThickness - height);
148             // top is untouched
149         }
150     }
151     if (decoration & TextDecoration::LineThrough) {
152         float baseline = lineStyle.fontMetrics().floatAscent();
153         if (decorationStyle == TextDecorationStyle::Wavy) {
154             extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + wavyStrokeParameters.controlPointDistance + strokeThickness - height);
155             extendIntToFloat(overflowResult.top, -(2 * baseline / 3 - wavyStrokeParameters.controlPointDistance - strokeThickness));
156         } else {
157             extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + strokeThickness - height);
158             extendIntToFloat(overflowResult.top, -(2 * baseline / 3));
159         }
160     }
161     return overflowResult;
162 }
163     
164 }