c9495085d6cc04da946da76d8225816937d99d27
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLRadicalOperator.cpp
1 /*
2  * Copyright (C) 2014 Frédéric Wang (fred.wang@free.fr). 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY 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
28 #if ENABLE(MATHML)
29 #include "RenderMathMLRadicalOperator.h"
30
31 namespace WebCore {
32
33 using namespace MathMLNames;
34
35 static const UChar gRadicalCharacter = 0x221A;
36
37 // This class relies on the RenderMathMLOperator class to draw a radical symbol.
38 // This does not work well when an OpenType MATH font is not available.
39 // In that case, we fallback to the old implementation of RenderMathMLRoot.cpp with graphic primitives.
40
41 // Normal width of the front of the radical sign, before the base & overbar (em)
42 const float gFrontWidthEms = 0.75f;
43 // Horizontal position of the bottom point of the radical (* frontWidth)
44 const float gRadicalBottomPointXFront = 0.5f;
45 // Lower the radical sign's bottom point (px)
46 const int gRadicalBottomPointLower = 3;
47 // Horizontal position of the top left point of the radical "dip" (* frontWidth)
48 const float gRadicalDipLeftPointXFront = 0.8f;
49 // Vertical position of the top left point of a sqrt radical "dip" (* baseHeight)
50 const float gSqrtRadicalDipLeftPointYPos = 0.5f;
51 // Vertical shift of the left end point of the radical (em)
52 const float gRadicalLeftEndYShiftEms = 0.05f;
53
54 // Radical line thickness (em)
55 const float gRadicalLineThicknessEms = 0.02f;
56 // Radical thick line thickness (em)
57 const float gRadicalThickLineThicknessEms = 0.1f;
58
59 RenderMathMLRadicalOperator::RenderMathMLRadicalOperator(Document& document, Ref<RenderStyle>&& style)
60     : RenderMathMLOperator(document, WTF::move(style), String(&gRadicalCharacter, 1), MathMLOperatorDictionary::Prefix)
61 {
62 }
63
64 void RenderMathMLRadicalOperator::stretchTo(LayoutUnit heightAboveBaseline, LayoutUnit depthBelowBaseline)
65 {
66     const auto& primaryFontData = style().font().primaryFont();
67     if (!primaryFontData || !primaryFontData->mathData()) {
68         // If we do not have an OpenType MATH font, we always make the radical depth a bit larger than the target.
69         depthBelowBaseline += gRadicalBottomPointLower;
70     }
71
72     RenderMathMLOperator::stretchTo(heightAboveBaseline, depthBelowBaseline);
73 }
74
75 void RenderMathMLRadicalOperator::setOperatorProperties()
76 {
77     RenderMathMLOperator::setOperatorProperties();
78     // We remove spacing around the radical symbol.
79     setLeadingSpace(0);
80     setTrailingSpace(0);
81 }
82
83 void RenderMathMLRadicalOperator::computePreferredLogicalWidths()
84 {
85     ASSERT(preferredLogicalWidthsDirty());
86
87     const auto& primaryFontData = style().font().primaryFont();
88     if (primaryFontData && primaryFontData->mathData()) {
89         RenderMathMLOperator::computePreferredLogicalWidths();
90         return;
91     }
92
93     // If we do not have an OpenType MATH font, the front width is just given by the gFrontWidthEms constant.
94     int frontWidth = lroundf(gFrontWidthEms * style().fontSize());
95     m_minPreferredLogicalWidth = frontWidth;
96     m_maxPreferredLogicalWidth = frontWidth;
97 }
98
99 void RenderMathMLRadicalOperator::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
100 {
101     const auto& primaryFontData = style().font().primaryFont();
102     if (primaryFontData && primaryFontData->mathData()) {
103         RenderMathMLOperator::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
104         return;
105     }
106
107     // If we do not have an OpenType MATH font, the logical height is always the stretch size.
108     logicalHeight = stretchSize();
109     RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
110 }
111
112 void RenderMathMLRadicalOperator::paint(PaintInfo& info, const LayoutPoint& paintOffset)
113 {
114     if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE)
115         return;
116
117     const auto& primaryFontData = style().font().primaryFont();
118     if (primaryFontData && primaryFontData->mathData()) {
119         RenderMathMLOperator::paint(info, paintOffset);
120         return;
121     }
122
123     // If we do not have an OpenType MATH font, we paint the radical sign with graphic primitives.
124     IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location() + contentBoxRect().location());
125     int frontWidth = lroundf(gFrontWidthEms * style().fontSize());
126     int startX = adjustedPaintOffset.x() + frontWidth;
127     int baseHeight = stretchSize() - gRadicalBottomPointLower;
128
129     float radicalDipLeftPointYPos = gSqrtRadicalDipLeftPointYPos * baseHeight;
130
131     FloatPoint overbarLeftPoint(startX, adjustedPaintOffset.y());
132     FloatPoint bottomPoint(startX - gRadicalBottomPointXFront * frontWidth, adjustedPaintOffset.y() + baseHeight + gRadicalBottomPointLower);
133     FloatPoint dipLeftPoint(startX - gRadicalDipLeftPointXFront * frontWidth, adjustedPaintOffset.y() + radicalDipLeftPointYPos);
134     FloatPoint leftEnd(startX - frontWidth, dipLeftPoint.y() + gRadicalLeftEndYShiftEms * style().fontSize());
135
136     GraphicsContextStateSaver stateSaver(*info.context);
137
138     info.context->setStrokeThickness(gRadicalLineThicknessEms * style().fontSize());
139     info.context->setStrokeStyle(SolidStroke);
140     info.context->setStrokeColor(style().visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB);
141     info.context->setLineJoin(MiterJoin);
142     info.context->setMiterLimit(style().fontSize());
143
144     Path root;
145
146     root.moveTo(FloatPoint(overbarLeftPoint.x(), adjustedPaintOffset.y()));
147     // draw from top left corner to bottom point of radical
148     root.addLineTo(bottomPoint);
149     // draw from bottom point to top of left part of radical base "dip"
150     root.addLineTo(dipLeftPoint);
151     // draw to end
152     root.addLineTo(leftEnd);
153
154     info.context->strokePath(root);
155
156     GraphicsContextStateSaver maskStateSaver(*info.context);
157
158     // Build a mask to draw the thick part of the root.
159     Path mask;
160
161     mask.moveTo(overbarLeftPoint);
162     mask.addLineTo(bottomPoint);
163     mask.addLineTo(dipLeftPoint);
164     mask.addLineTo(FloatPoint(2 * dipLeftPoint.x() - leftEnd.x(), 2 * dipLeftPoint.y() - leftEnd.y()));
165
166     info.context->clip(mask);
167
168     // Draw the thick part of the root.
169     info.context->setStrokeThickness(gRadicalThickLineThicknessEms * style().fontSize());
170     info.context->setLineCap(SquareCap);
171
172     Path line;
173     line.moveTo(bottomPoint);
174     line.addLineTo(dipLeftPoint);
175
176     info.context->strokePath(line);
177 }
178
179 }
180
181 #endif