summaryrefslogtreecommitdiff
path: root/Source/WebCore/style/InlineTextBoxStyle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/style/InlineTextBoxStyle.cpp')
-rw-r--r--Source/WebCore/style/InlineTextBoxStyle.cpp166
1 files changed, 166 insertions, 0 deletions
diff --git a/Source/WebCore/style/InlineTextBoxStyle.cpp b/Source/WebCore/style/InlineTextBoxStyle.cpp
new file mode 100644
index 000000000..b411c0891
--- /dev/null
+++ b/Source/WebCore/style/InlineTextBoxStyle.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InlineTextBoxStyle.h"
+
+#include "FontCascade.h"
+#include "InlineTextBox.h"
+#include "RootInlineBox.h"
+
+namespace WebCore {
+
+int computeUnderlineOffset(TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, const InlineTextBox* inlineTextBox, int textDecorationThickness)
+{
+ // This represents the gap between the baseline and the closest edge of the underline.
+ int gap = std::max<int>(1, ceilf(textDecorationThickness / 2.0));
+
+ // FIXME: The code for visual overflow detection passes in a null inline text box. This means it is now
+ // broken for the case where auto needs to behave like "under".
+
+ // According to the specification TextUnderlinePositionAuto should avoid drawing through glyphs in
+ // scripts where it would not be appropriate (e.g., ideographs).
+ // Strictly speaking this can occur whenever the line contains ideographs
+ // even if it is horizontal, but detecting this has performance implications. For now we only work with
+ // vertical text, since we already determined the baseline type to be ideographic in that
+ // case.
+
+ TextUnderlinePosition resolvedUnderlinePosition = underlinePosition;
+ if (resolvedUnderlinePosition == TextUnderlinePositionAuto) {
+ if (inlineTextBox)
+ resolvedUnderlinePosition = inlineTextBox->root().baselineType() == IdeographicBaseline ? TextUnderlinePositionUnder : TextUnderlinePositionAlphabetic;
+ else
+ resolvedUnderlinePosition = TextUnderlinePositionAlphabetic;
+ }
+
+ switch (resolvedUnderlinePosition) {
+ case TextUnderlinePositionAlphabetic:
+ return fontMetrics.ascent() + gap;
+ case TextUnderlinePositionUnder: {
+ ASSERT(inlineTextBox);
+ // Position underline relative to the bottom edge of the lowest element's content box.
+ const RootInlineBox& rootBox = inlineTextBox->root();
+ const RenderElement* decorationRenderer = inlineTextBox->parent()->renderer().enclosingRendererWithTextDecoration(TextDecorationUnderline, inlineTextBox->isFirstLine());
+
+ float offset;
+ if (inlineTextBox->renderer().style().isFlippedLinesWritingMode()) {
+ offset = inlineTextBox->logicalTop();
+ rootBox.minLogicalTopForTextDecorationLine(offset, decorationRenderer, TextDecorationUnderline);
+ offset = inlineTextBox->logicalTop() - offset;
+ } else {
+ offset = inlineTextBox->logicalBottom();
+ rootBox.maxLogicalBottomForTextDecorationLine(offset, decorationRenderer, TextDecorationUnderline);
+ offset -= inlineTextBox->logicalBottom();
+ }
+ return inlineTextBox->logicalHeight() + gap + std::max<float>(offset, 0);
+ }
+ case TextUnderlinePositionAuto:
+ ASSERT_NOT_REACHED();
+ }
+
+ ASSERT_NOT_REACHED();
+ return fontMetrics.ascent() + gap;
+}
+
+void getWavyStrokeParameters(float strokeThickness, float& controlPointDistance, float& step)
+{
+ // Distance between decoration's axis and Bezier curve's control points.
+ // The height of the curve is based on this distance. Use a minimum of 6 pixels distance since
+ // the actual curve passes approximately at half of that distance, that is 3 pixels.
+ // The minimum height of the curve is also approximately 3 pixels. Increases the curve's height
+ // as strokeThickness increases to make the curve look better.
+ controlPointDistance = 3 * std::max<float>(2, strokeThickness);
+
+ // Increment used to form the diamond shape between start point (p1), control
+ // points and end point (p2) along the axis of the decoration. Makes the
+ // curve wider as strokeThickness increases to make the curve look better.
+ step = 2 * std::max<float>(2, strokeThickness);
+}
+
+static inline void extendIntToFloat(int& extendMe, float extendTo)
+{
+ extendMe = std::max(extendMe, static_cast<int>(ceilf(extendTo)));
+}
+
+GlyphOverflow visualOverflowForDecorations(const RenderStyle& lineStyle, const InlineTextBox* inlineTextBox)
+{
+ ASSERT(!inlineTextBox || inlineTextBox->lineStyle() == lineStyle);
+
+ TextDecoration decoration = lineStyle.textDecorationsInEffect();
+ if (decoration == TextDecorationNone)
+ return GlyphOverflow();
+
+ float strokeThickness = textDecorationStrokeThickness(lineStyle.fontSize());
+ float controlPointDistance = 0;
+ float step;
+ float wavyOffset = 0;
+
+ TextDecorationStyle decorationStyle = lineStyle.textDecorationStyle();
+ float height = lineStyle.fontCascade().fontMetrics().floatHeight();
+ GlyphOverflow overflowResult;
+
+ if (decorationStyle == TextDecorationStyleWavy) {
+ getWavyStrokeParameters(strokeThickness, controlPointDistance, step);
+ wavyOffset = wavyOffsetFromDecoration();
+ overflowResult.left = strokeThickness;
+ overflowResult.right = strokeThickness;
+ }
+
+ // These metrics must match where underlines get drawn.
+ if (decoration & TextDecorationUnderline) {
+ // Compensate for the integral ceiling in GraphicsContext::computeLineBoundsAndAntialiasingModeForText()
+ int underlineOffset = 1;
+ underlineOffset += computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.fontMetrics(), inlineTextBox, strokeThickness);
+ if (decorationStyle == TextDecorationStyleWavy) {
+ extendIntToFloat(overflowResult.bottom, underlineOffset + wavyOffset + controlPointDistance + strokeThickness - height);
+ extendIntToFloat(overflowResult.top, -(underlineOffset + wavyOffset - controlPointDistance - strokeThickness));
+ } else {
+ extendIntToFloat(overflowResult.bottom, underlineOffset + strokeThickness - height);
+ extendIntToFloat(overflowResult.top, -underlineOffset);
+ }
+ }
+ if (decoration & TextDecorationOverline) {
+ if (decorationStyle == TextDecorationStyleWavy) {
+ extendIntToFloat(overflowResult.bottom, -wavyOffset + controlPointDistance + strokeThickness - height);
+ extendIntToFloat(overflowResult.top, wavyOffset + controlPointDistance + strokeThickness);
+ } else {
+ extendIntToFloat(overflowResult.bottom, strokeThickness - height);
+ // top is untouched
+ }
+ }
+ if (decoration & TextDecorationLineThrough) {
+ float baseline = lineStyle.fontMetrics().floatAscent();
+ if (decorationStyle == TextDecorationStyleWavy) {
+ extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + controlPointDistance + strokeThickness - height);
+ extendIntToFloat(overflowResult.top, -(2 * baseline / 3 - controlPointDistance - strokeThickness));
+ } else {
+ extendIntToFloat(overflowResult.bottom, 2 * baseline / 3 + strokeThickness - height);
+ extendIntToFloat(overflowResult.top, -(2 * baseline / 3));
+ }
+ }
+ return overflowResult;
+}
+
+}