MathML internals - improve naming in RenderMathMLSquareRoot.cpp and RenderMathMLRoot.cpp
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLSquareRoot.cpp
1 /*
2  * Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved.
3  * Copyright (C) 2010 Fran├žois Sausset (sausset@gmail.com). All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28
29 #if ENABLE(MATHML)
30
31 #include "RenderMathMLSquareRoot.h"
32
33 #include "GraphicsContext.h"
34 #include "MathMLNames.h"
35 #include "PaintInfo.h"
36
37 namespace WebCore {
38     
39 using namespace MathMLNames;
40
41 // Lower the radical sign's bottom point (px)
42 const int gRadicalBottomPointLower = 3;
43 // Threshold above which the radical shape is modified to look nice with big bases (em)
44 const float gThresholdBaseHeightEms = 1.5f;
45 // Front width (em)
46 const float gFrontWidthEms = 0.75f;
47 // Horizontal position of the bottom point of the radical (* frontWidth)
48 const float gRadicalBottomPointXFront = 0.5f;
49 // Horizontal position of the top left point of the radical "dip" (* frontWidth)
50 const float gRadicalDipLeftPointXFront = 0.2f;
51 // Vertical position of the top left point of the radical "dip" (* baseHeight)
52 const float gRadicalDipLeftPointYPos = 0.5f; 
53 // Vertical shift of the left end point of the radical (em)
54 const float gRadicalLeftEndYShiftEms = 0.05f;
55 // Additional bottom root padding if baseHeight > threshold (em)
56 const float gBigRootBottomPaddingEms = 0.2f;
57
58 // Radical line thickness (em)
59 const float gRadicalLineThicknessEms = 0.02f;
60 // Radical thick line thickness (em)
61 const float gRadicalThickLineThicknessEms = 0.1f;
62     
63 RenderMathMLSquareRoot::RenderMathMLSquareRoot(Element* element)
64     : RenderMathMLBlock(element)
65 {
66 }
67
68 void RenderMathMLSquareRoot::paint(PaintInfo& info, const LayoutPoint& paintOffset)
69 {
70     RenderMathMLBlock::paint(info, paintOffset);
71     
72     if (info.context->paintingDisabled())
73         return;
74     
75     IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location());
76     
77     int baseHeight = 0;
78     int overbarWidth = 0;
79     RenderObject* current = firstChild();
80     while (current) {
81         if (current->isBoxModelObject()) {
82             RenderBoxModelObject* box = toRenderBoxModelObject(current);
83             
84             // Check to see if this box has a larger height
85             // FIXME: We should consider the height above & below the baseline separately. This will be
86             // fixed by an <mrow> base wrapper, which is required anyway by the MathML spec.
87             if (box->pixelSnappedOffsetHeight() > baseHeight)
88                 baseHeight = box->pixelSnappedOffsetHeight();
89             overbarWidth += box->pixelSnappedOffsetWidth();
90         }
91         current = current->nextSibling();
92     }
93     // default to the font size in pixels if we're empty
94     if (!baseHeight)
95         baseHeight = style()->fontSize();
96     
97     int frontWidth = static_cast<int>(style()->fontSize() * gFrontWidthEms);
98     int overbarLeftPointShift = 0;
99     // Base height above which the shape of the root changes
100     int thresholdHeight = static_cast<int>(gThresholdBaseHeightEms * style()->fontSize());
101     
102     if (baseHeight > thresholdHeight && thresholdHeight) {
103         float shift = (baseHeight - thresholdHeight) / static_cast<float>(thresholdHeight);
104         if (shift > 1.)
105             shift = 1.0f;
106         overbarLeftPointShift = static_cast<int>(gRadicalBottomPointXFront * frontWidth * shift);
107     }
108     
109     overbarWidth += overbarLeftPointShift;
110     
111     FloatPoint overbarLeftPoint(adjustedPaintOffset.x() + frontWidth - overbarLeftPointShift, adjustedPaintOffset.y());
112     FloatPoint bottomPoint(adjustedPaintOffset.x() + frontWidth * gRadicalBottomPointXFront, adjustedPaintOffset.y() + baseHeight + gRadicalBottomPointLower);
113     FloatPoint dipLeftPoint(adjustedPaintOffset.x() + frontWidth * gRadicalDipLeftPointXFront, adjustedPaintOffset.y() + gRadicalDipLeftPointYPos * baseHeight);
114     FloatPoint leftEnd(adjustedPaintOffset.x(), dipLeftPoint.y() + gRadicalLeftEndYShiftEms * style()->fontSize());
115     
116     GraphicsContextStateSaver stateSaver(*info.context);
117     
118     info.context->setStrokeThickness(gRadicalLineThicknessEms * style()->fontSize());
119     info.context->setStrokeStyle(SolidStroke);
120     info.context->setStrokeColor(style()->visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB);
121     info.context->setLineJoin(MiterJoin);
122     info.context->setMiterLimit(style()->fontSize());
123     
124     Path root;
125     
126     root.moveTo(FloatPoint(overbarLeftPoint.x() + overbarWidth, adjustedPaintOffset.y()));
127     // draw top
128     root.addLineTo(overbarLeftPoint);
129     // draw from top left corner to bottom point of radical
130     root.addLineTo(bottomPoint);
131     // draw from bottom point to top of left part of radical base "dip"
132     root.addLineTo(dipLeftPoint);
133     // draw to end
134     root.addLineTo(leftEnd);
135     
136     info.context->strokePath(root);
137     
138     GraphicsContextStateSaver maskStateSaver(*info.context);
139     
140     // Build a mask to draw the thick part of the root.
141     Path mask;
142     
143     mask.moveTo(overbarLeftPoint);
144     mask.addLineTo(bottomPoint);
145     mask.addLineTo(dipLeftPoint);
146     mask.addLineTo(FloatPoint(2 * dipLeftPoint.x() - leftEnd.x(), 2 * dipLeftPoint.y() - leftEnd.y()));
147     
148     info.context->clip(mask);
149     
150     // Draw the thick part of the root.
151     info.context->setStrokeThickness(gRadicalThickLineThicknessEms * style()->fontSize());
152     info.context->setLineCap(SquareCap);
153     
154     Path line;
155     line.moveTo(bottomPoint);
156     line.addLineTo(dipLeftPoint);
157     
158     info.context->strokePath(line);
159 }
160
161 void RenderMathMLSquareRoot::layout()
162 {
163     int baseHeight = 0;
164     
165     RenderObject* current = firstChild();
166     while (current) {
167         if (current->isBoxModelObject()) {
168             RenderBoxModelObject* box = toRenderBoxModelObject(current);
169             
170             if (box->pixelSnappedOffsetHeight() > baseHeight)
171                 baseHeight = box->pixelSnappedOffsetHeight();
172             
173             // FIXME: Can box->style() be modified?
174             box->style()->setVerticalAlign(BASELINE);
175         }
176         current = current->nextSibling();
177     }
178     
179     if (!baseHeight)
180         baseHeight = style()->fontSize();
181     
182     // FIXME: Can style() be modified? And don't we need styleDidChange()?
183     if (baseHeight > static_cast<int>(gThresholdBaseHeightEms * style()->fontSize()))
184         style()->setPaddingBottom(Length(static_cast<int>(gBigRootBottomPaddingEms * style()->fontSize()), Fixed));
185     
186     RenderBlock::layout();
187 }
188     
189 }
190
191 #endif // ENABLE(MATHML)