2 * Copyright (C) 2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "InlineTextBoxStyle.h"
30 #include "InlineTextBox.h"
31 #include "RootInlineBox.h"
35 int computeUnderlineOffset(TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, InlineTextBox* inlineTextBox, int textDecorationThickness)
37 // This represents the gap between the baseline and the closest edge of the underline.
38 int gap = std::max<int>(1, ceilf(textDecorationThickness / 2.0));
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);
55 return fontMetrics.ascent() + gap;
58 void getWavyStrokeParameters(float strokeThickness, float& controlPointDistance, float& step)
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);
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);
73 static inline void extendIntToFloat(int& extendMe, float extendTo)
75 extendMe = std::max(extendMe, static_cast<int>(ceilf(extendTo)));
78 GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, InlineTextBox* inlineTextBox)
80 ASSERT(!inlineTextBox || inlineTextBox->lineStyle() == lineStyle);
82 TextDecoration decoration = lineStyle.textDecorationsInEffect();
83 if (decoration == TextDecorationNone)
84 return GlyphOverflow();
86 float strokeThickness = textDecorationStrokeThickness(lineStyle.fontSize());
87 float controlPointDistance;
91 TextDecorationStyle decorationStyle = lineStyle.textDecorationStyle();
92 float height = lineStyle.font().fontMetrics().floatHeight();
93 GlyphOverflow overflowResult;
95 if (decorationStyle == TextDecorationStyleWavy) {
96 getWavyStrokeParameters(strokeThickness, controlPointDistance, step);
97 wavyOffset = wavyOffsetFromDecoration();
98 overflowResult.left = strokeThickness;
99 overflowResult.right = strokeThickness;
102 // These metrics must match where underlines get drawn.
103 if (decoration & TextDecorationUnderline) {
104 float underlineOffset = computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.fontMetrics(), inlineTextBox, strokeThickness);
105 if (decorationStyle == TextDecorationStyleWavy) {
106 extendIntToFloat(overflowResult.bottom, underlineOffset + wavyOffset + controlPointDistance + strokeThickness - height);
107 extendIntToFloat(overflowResult.top, -(underlineOffset + wavyOffset - controlPointDistance - strokeThickness));
109 extendIntToFloat(overflowResult.bottom, underlineOffset + strokeThickness - height);
110 extendIntToFloat(overflowResult.top, -underlineOffset);
113 if (decoration & TextDecorationOverline) {
114 if (decorationStyle == TextDecorationStyleWavy) {
115 extendIntToFloat(overflowResult.bottom, -wavyOffset + controlPointDistance + strokeThickness - height);
116 extendIntToFloat(overflowResult.top, wavyOffset + controlPointDistance + strokeThickness);
118 extendIntToFloat(overflowResult.bottom, strokeThickness - height);
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));
128 extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + strokeThickness - height);
129 extendIntToFloat(overflowResult.top, -(2 * baseline / 3));
132 return overflowResult;