0bd83a7f5122e52becf41dd9f777ae081a88919d
[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 #include "Path.h"
37
38 namespace WebCore {
39     
40 using namespace MathMLNames;
41
42 // Bottom padding of the radical (px)
43 const int gRadicalBasePad = 3;
44 // Threshold above which the radical shape is modified to look nice with big bases (%)
45 const float gThresholdBaseHeight = 1.5f;
46 // Radical width (%)
47 const float gRadicalWidth = 0.75f;
48 // Horizontal position of the bottom point of the radical (%)
49 const float gRadicalBottomPointXPos= 0.5f;
50 // Horizontal position of the top left point of the radical (%)
51 const float gRadicalTopLeftPointXPos = 0.2f;
52 // Vertical position of the top left point of the radical (%)
53 const float gRadicalTopLeftPointYPos = 0.5f; 
54 // Vertical shift of the left end point of the radical (%)
55 const float gRadicalLeftEndYShift = 0.05f;
56 // Additional bottom root padding (%)
57 const float gRootBottomPadding = 0.2f;
58
59 // Radical line thickness (%)
60 const float gRadicalLineThickness = 0.02f;
61 // Radical thick line thickness (%)
62 const float gRadicalThickLineThickness = 0.1f;
63     
64 RenderMathMLSquareRoot::RenderMathMLSquareRoot(Element* element)
65     : RenderMathMLBlock(element)
66 {
67 }
68
69 void RenderMathMLSquareRoot::paint(PaintInfo& info, const LayoutPoint& paintOffset)
70 {
71     RenderMathMLBlock::paint(info, paintOffset);
72    
73     if (info.context->paintingDisabled())
74         return;
75     
76     IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location());
77
78     int maxHeight = 0;
79     int width = 0;
80     RenderObject* current = firstChild();
81     while (current) {
82         if (current->isBoxModelObject()) {
83             
84             RenderBoxModelObject* box = toRenderBoxModelObject(current);
85             
86             // Check to see if this box has a larger height
87             if (box->pixelSnappedOffsetHeight() > maxHeight)
88                 maxHeight = box->pixelSnappedOffsetHeight();
89             width += box->pixelSnappedOffsetWidth();
90         }
91         current = current->nextSibling();
92     }
93     // default to the font size in pixels if we're empty
94     if (!maxHeight)
95         maxHeight = style()->fontSize();
96     
97     int frontWidth = static_cast<int>(style()->fontSize() * gRadicalWidth);
98     int topStartShift = 0;
99     // Base height above which the shape of the root changes
100     int thresholdHeight = static_cast<int>(gThresholdBaseHeight * style()->fontSize());
101     
102     if (maxHeight > thresholdHeight && thresholdHeight) {
103         float shift = (maxHeight - thresholdHeight) / static_cast<float>(thresholdHeight);
104         if (shift > 1.)
105             shift = 1.0f;
106         topStartShift = static_cast<int>(gRadicalBottomPointXPos * frontWidth * shift);
107     }
108     
109     width += topStartShift;
110     
111     FloatPoint topStart(adjustedPaintOffset.x() + frontWidth - topStartShift, adjustedPaintOffset.y());
112     FloatPoint bottomLeft(adjustedPaintOffset.x() + frontWidth * gRadicalBottomPointXPos , adjustedPaintOffset.y() + maxHeight + gRadicalBasePad);
113     FloatPoint topLeft(adjustedPaintOffset.x() + frontWidth * gRadicalTopLeftPointXPos , adjustedPaintOffset.y() + gRadicalTopLeftPointYPos * maxHeight);
114     FloatPoint leftEnd(adjustedPaintOffset.x() , topLeft.y() + gRadicalLeftEndYShift * style()->fontSize());
115     
116     GraphicsContextStateSaver stateSaver(*info.context);
117     
118     info.context->setStrokeThickness(gRadicalLineThickness * 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(topStart.x() + width , adjustedPaintOffset.y()));
127     // draw top
128     root.addLineTo(topStart);
129     // draw from top left corner to bottom point of radical
130     root.addLineTo(bottomLeft);
131     // draw from bottom point to top of left part of radical base "pocket"
132     root.addLineTo(topLeft);
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(topStart);
144     mask.addLineTo(bottomLeft);
145     mask.addLineTo(topLeft);
146     mask.addLineTo(FloatPoint(2 * topLeft.x() - leftEnd.x(), 2 * topLeft.y() - leftEnd.y()));
147     
148     info.context->clip(mask);
149     
150     // Draw the thick part of the root.
151     info.context->setStrokeThickness(gRadicalThickLineThickness * style()->fontSize());
152     info.context->setLineCap(SquareCap);
153     
154     Path line;
155     line.moveTo(bottomLeft);
156     line.addLineTo(topLeft);
157     
158     info.context->strokePath(line);
159 }
160
161 void RenderMathMLSquareRoot::layout()
162 {
163     int maxHeight = 0;
164     
165     RenderObject* current = firstChild();
166     while (current) {
167         if (current->isBoxModelObject()) {
168             RenderBoxModelObject* box = toRenderBoxModelObject(current);
169             
170             if (box->pixelSnappedOffsetHeight() > maxHeight)
171                 maxHeight = box->pixelSnappedOffsetHeight();
172             
173             box->style()->setVerticalAlign(BASELINE);
174         }
175         current = current->nextSibling();
176     }
177     
178     if (!maxHeight)
179         maxHeight = style()->fontSize();
180
181     
182     if (maxHeight > static_cast<int>(gThresholdBaseHeight * style()->fontSize()))
183         style()->setPaddingBottom(Length(static_cast<int>(gRootBottomPadding * style()->fontSize()), Fixed));
184
185     
186     RenderBlock::layout();
187 }
188     
189 }
190
191 #endif // ENABLE(MATHML)