34e54c28e4ab45799d1f213ca3b7660e9201a4d7
[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 "Font.h"
30 #include "InlineTextBox.h"
31 #include "RootInlineBox.h"
32
33 namespace WebCore {
34     
35 int computeUnderlineOffset(TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, InlineTextBox* inlineTextBox)
36 {
37     // This represents the gap between the baseline and the closest edge of the underline.
38     float gap = fontMetrics.underlinePosition();
39
40     // According to the specification TextUnderlinePositionAuto should default to 'alphabetic' for horizontal text
41     // and to 'under Left' for vertical text (e.g. japanese). We support only horizontal text for now.
42     switch (underlinePosition) {
43     case TextUnderlinePositionAlphabetic:
44     case TextUnderlinePositionAuto:
45         return fontMetrics.ascent() + gap;
46     case TextUnderlinePositionUnder: {
47         ASSERT(inlineTextBox);
48         // Position underline relative to the bottom edge of the lowest element's content box.
49         float offset = inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop();
50         return inlineTextBox->logicalHeight() + gap + std::max<float>(offset, 0);
51     }
52     }
53
54     ASSERT_NOT_REACHED();
55     return fontMetrics.ascent() + gap;
56 }
57     
58 void getWavyStrokeParameters(float strokeThickness, float& controlPointDistance, float& step)
59 {
60     // Distance between decoration's axis and Bezier curve's control points.
61     // The height of the curve is based on this distance. Use a minimum of 6 pixels distance since
62     // the actual curve passes approximately at half of that distance, that is 3 pixels.
63     // The minimum height of the curve is also approximately 3 pixels. Increases the curve's height
64     // as strokeThickness increases to make the curve look better.
65     controlPointDistance = 3 * std::max<float>(2, strokeThickness);
66
67     // Increment used to form the diamond shape between start point (p1), control
68     // points and end point (p2) along the axis of the decoration. Makes the
69     // curve wider as strokeThickness increases to make the curve look better.
70     step = 2 * std::max<float>(2, strokeThickness);
71 }
72
73 static inline void extendIntToFloat(int& extendMe, float extendTo)
74 {
75     extendMe = std::max(extendMe, static_cast<int>(ceilf(extendTo)));
76 }
77
78 GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, InlineTextBox* inlineTextBox)
79 {
80     ASSERT(!inlineTextBox || inlineTextBox->lineStyle() == lineStyle);
81     
82     TextDecoration decoration = lineStyle.textDecorationsInEffect();
83     if (decoration == TextDecorationNone)
84         return GlyphOverflow();
85     
86     float strokeThickness = lineStyle.fontMetrics().decorationThickness();
87     float controlPointDistance;
88     float step;
89     float wavyOffset;
90         
91     TextDecorationStyle decorationStyle = lineStyle.textDecorationStyle();
92     float height = lineStyle.font().fontMetrics().floatHeight();
93     GlyphOverflow overflowResult;
94     
95     if (decorationStyle == TextDecorationStyleWavy) {
96         getWavyStrokeParameters(strokeThickness, controlPointDistance, step);
97         wavyOffset = wavyOffsetFromDecoration();
98         overflowResult.left = strokeThickness;
99         overflowResult.right = strokeThickness;
100     }
101
102     // These metrics must match where underlines get drawn.
103     if (decoration & TextDecorationUnderline) {
104         float underlineOffset = computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.fontMetrics(), inlineTextBox);
105         if (decorationStyle == TextDecorationStyleWavy) {
106             extendIntToFloat(overflowResult.bottom, underlineOffset + wavyOffset + controlPointDistance + strokeThickness - height);
107             extendIntToFloat(overflowResult.top, -(underlineOffset + wavyOffset - controlPointDistance - strokeThickness));
108         } else {
109             extendIntToFloat(overflowResult.bottom, underlineOffset + strokeThickness - height);
110             extendIntToFloat(overflowResult.top, -underlineOffset);
111         }
112     }
113     if (decoration & TextDecorationOverline) {
114         if (decorationStyle == TextDecorationStyleWavy) {
115             extendIntToFloat(overflowResult.bottom, -wavyOffset + controlPointDistance + strokeThickness - height);
116             extendIntToFloat(overflowResult.top, wavyOffset + controlPointDistance + strokeThickness);
117         } else {
118             extendIntToFloat(overflowResult.bottom, strokeThickness - height);
119             // top is untouched
120         }
121     }
122     if (decoration & TextDecorationLineThrough) {
123         float baseline = lineStyle.fontMetrics().floatAscent();
124         if (decorationStyle == TextDecorationStyleWavy) {
125             extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + controlPointDistance + strokeThickness - height);
126             extendIntToFloat(overflowResult.top, -(2 * baseline / 3 - controlPointDistance - strokeThickness));
127         } else {
128             extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + strokeThickness - height);
129             extendIntToFloat(overflowResult.top, -(2 * baseline / 3));
130         }
131     }
132     return overflowResult;
133 }
134     
135 }