Build fix.
[WebKit-https.git] / WebCore / kcanvas / SVGInlineFlowBox.cpp
1 /*
2  * This file is part of the WebKit project.
3  *
4  * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
5  *           (C) 2006 Apple Computer Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  */
23
24
25 #ifdef SVG_SUPPORT
26
27 #include "config.h"
28 #include "SVGInlineFlowBox.h"
29 #include "KRenderingDevice.h"
30 #include "KCanvasRenderingStyle.h"
31 #include "KCanvasClipper.h"
32 #include "KCanvasMasker.h"
33 #include "InlineTextBox.h"
34 #include "RootInlineBox.h"
35 #include "SVGTextPositioningElement.h"
36 #include "SVGLengthList.h"
37 #include "GraphicsContext.h"
38 #include <wtf/OwnPtr.h>
39
40 using namespace std;
41
42 namespace WebCore {
43
44 void SVGInlineFlowBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) 
45 {
46     paintSVGInlineFlow(this, object(), paintInfo, tx, ty);
47 }
48
49 int SVGInlineFlowBox::placeBoxesHorizontally(int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing)
50 {
51     return placeSVGFlowHorizontally(this, x, leftPosition, rightPosition, needsWordSpacing);
52 }
53
54 void SVGInlineFlowBox::verticallyAlignBoxes(int& heightOfBlock)
55 {
56     placeSVGFlowVertically(this, heightOfBlock);
57 }
58
59 void paintSVGInlineFlow(InlineFlowBox* flow, RenderObject* object, RenderObject::PaintInfo& paintInfo, int tx, int ty)
60 {
61     if (paintInfo.p->paintingDisabled())
62         return;
63     
64     KRenderingDevice* device = renderingDevice();
65     KRenderingDeviceContext* context = device->currentContext();
66     bool shouldPopContext = false;
67     if (context)
68         paintInfo.p->save();
69     else {
70         // Need to set up KCanvas rendering if it hasn't already been done.
71         context = paintInfo.p->createRenderingDeviceContext();
72         device->pushContext(context);
73         shouldPopContext = true;
74     }
75     
76
77     context->concatCTM(object->localTransform());
78     
79     FloatRect boundingBox(tx+flow->xPos() , ty+flow->yPos(), flow->width(), flow->height());
80     
81     const SVGRenderStyle* svgStyle = object->style()->svgStyle();
82     if (KCanvasClipper* clipper = getClipperById(object->document(), svgStyle->clipPath().substring(1)))
83         clipper->applyClip(boundingBox);
84     
85     if (KCanvasMasker* masker = getMaskerById(object->document(), svgStyle->maskElement().substring(1)))
86         masker->applyMask(boundingBox);
87     
88     KCanvasFilter* filter = getFilterById(object->document(), svgStyle->filter().substring(1));
89     if (filter)
90         filter->prepareFilter(boundingBox);
91     
92     RenderObject::PaintInfo pi = paintInfo;
93     OwnPtr<GraphicsContext> c(device->currentContext()->createGraphicsContext());
94     pi.p = c.get();
95     if (!flow->isRootInlineBox())
96         pi.r = (object->localTransform()).invert().mapRect(paintInfo.r);
97     
98     float opacity = object->style()->opacity();
99     if (opacity < 1.0f) {
100         c->clip(enclosingIntRect(boundingBox));
101         c->beginTransparencyLayer(opacity);
102     }
103     
104     KRenderingPaintServer* fillPaintServer = KSVGPainterFactory::fillPaintServer(object->style(), object);
105     if (fillPaintServer) {
106         fillPaintServer->setPaintingText(true);
107         if (fillPaintServer->setup(context, object, APPLY_TO_FILL)) {
108             flow->InlineFlowBox::paint(pi, tx, ty);
109             fillPaintServer->teardown(context, object, APPLY_TO_FILL);
110         }
111         fillPaintServer->setPaintingText(false);
112     }
113     KRenderingPaintServer* strokePaintServer = KSVGPainterFactory::strokePaintServer(object->style(), object);
114     if (strokePaintServer) {
115         strokePaintServer->setPaintingText(true);
116         if (strokePaintServer->setup(context, object, APPLY_TO_STROKE)) {
117             flow->InlineFlowBox::paint(pi, tx, ty);
118             strokePaintServer->teardown(context, object, APPLY_TO_STROKE);
119         }
120         strokePaintServer->setPaintingText(false);
121     }
122     
123     if (filter) 
124         filter->applyFilter(boundingBox);
125     
126     if (opacity < 1.0f)
127         c->endTransparencyLayer();
128
129     // restore drawing state
130     if (!shouldPopContext)
131         paintInfo.p->restore();
132     else {
133         device->popContext();
134         delete context;
135     }
136 }
137
138 static bool translateBox(InlineBox* box, int x, int y, bool topLevel = true)
139 {    
140     if (box->object()->isText()) {
141         box->setXPos(box->xPos() + x);
142         box->setYPos(box->yPos() + y);
143     } else {
144         InlineFlowBox* flow = static_cast<InlineFlowBox*>(box);
145         SVGTextPositioningElement* text = static_cast<SVGTextPositioningElement*>(box->object()->element());
146         
147         if (topLevel||!(text->x()->getFirst() || text->y()->getFirst() || 
148                         (text->dx()->getFirst() && text->dx()->getFirst()->value()) ||
149                         (text->dy()->getFirst() && text->dy()->getFirst()->value()))) {
150             box->setXPos(box->xPos() + x);
151             box->setYPos(box->yPos() + y);
152             for (InlineBox* curr = flow->firstChild(); curr; curr = curr->nextOnLine()) 
153                 if (!translateBox(curr, x, y, false))
154                     return false;
155         }
156     }
157     return true;
158 }
159
160 static int placePositionedBoxesHorizontally(InlineFlowBox* flow, int x, int& leftPosition, int& rightPosition, int& leftAlign, int& rightAlign, bool& needsWordSpacing, int xPos, bool positioned)
161 {
162     //int startx = x;
163     int mn = INT_MAX;
164     int mx = INT_MIN;
165     int amn = INT_MAX;
166     int amx = INT_MIN;
167     int startx = x;
168     bool seenPositionedElement = false;
169     flow->setXPos(x);
170     for (InlineBox* curr = flow->firstChild(); curr; curr = curr->nextOnLine()) 
171     {
172         if (curr->object()->isText()) {
173             mn = min(mn, x);
174             amn = min(amn, x);
175             InlineTextBox* text = static_cast<InlineTextBox*>(curr);
176             RenderText* rt = static_cast<RenderText*>(text->object());
177             if (rt->length()) {
178                 if (needsWordSpacing && DeprecatedChar(rt->text()[text->start()]).isSpace())
179                     x += rt->font(flow->isFirstLineStyle())->wordSpacing();
180                 needsWordSpacing = !DeprecatedChar(rt->text()[text->end()]).isSpace();
181             }
182             text->setXPos(x);
183             x += text->width();
184             mx = max(mx, x);
185             amx = max(amx, x);
186         } else {
187             assert(curr->object()->isInlineFlow());
188             InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr);
189             SVGTextPositioningElement* text = static_cast<SVGTextPositioningElement*>(flow->object()->element());
190             x += (int)(text->dx()->getFirst() ? text->dx()->getFirst()->value() : 0);
191             if (text->x()->getFirst())
192                 x = (int)(text->x()->getFirst()->value() - xPos);
193             if (text->x()->getFirst() || text->y()->getFirst() || 
194                 (text->dx()->getFirst() && text->dx()->getFirst()->value()) ||
195                 (text->dy()->getFirst() && text->dy()->getFirst()->value())) {
196                 seenPositionedElement = true;
197                 needsWordSpacing = false;
198                 int ignoreX, ignoreY;
199                 x = placePositionedBoxesHorizontally(flow, x, mn, mx, ignoreX, ignoreY, needsWordSpacing, xPos, true);
200             } else if (seenPositionedElement) {
201                 int _amn, _amx; //throw away these values
202                 x = placePositionedBoxesHorizontally(flow, x, mn, mx, _amn, _amx, needsWordSpacing, xPos, false);
203             } else
204                 x = placePositionedBoxesHorizontally(flow, x, mn, mx, amn, amx, needsWordSpacing, xPos, false);
205         }
206     }
207     if (mn > mx)
208         mn = mx = startx;
209     if (amn > amx)
210         amn = amx = startx;
211     
212     int width = mx - mn;
213     flow->setWidth(width);
214     int awidth = amx - amn;
215     int dx=0;
216     if (positioned) {
217         switch (flow->object()->style()->svgStyle()->textAnchor()) 
218         {
219             case TA_MIDDLE:
220                 translateBox(flow, dx = -awidth/2, 0);            
221                 break;
222             case TA_END:
223                 translateBox(flow, dx = -awidth, 0);
224                 break;
225             case TA_START:
226             default:
227                 break;
228         }
229         if (dx) {
230             x += dx;
231             mn += dx;
232             mx += dx;
233         }
234     }
235     leftPosition = min(leftPosition, mn);
236     rightPosition = max(rightPosition, mx);
237     leftAlign = min(leftAlign, amn);
238     rightAlign = max(rightAlign, amx);
239     
240     return x;
241 }
242
243 int placeSVGFlowHorizontally(InlineFlowBox* flow, int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing)
244 {
245     int ignoreX, ignoreY;
246     x = placePositionedBoxesHorizontally(flow, x, leftPosition, rightPosition, ignoreX, ignoreY, needsWordSpacing, flow->object()->xPos(), true);
247     leftPosition = min(leftPosition, x);
248     rightPosition = max(rightPosition, x);
249     needsWordSpacing = false;
250     return x;
251 }
252
253 static void placeBoxesVerticallyWithAbsBaseline(InlineFlowBox* flow, int& heightOfBlock, int& min_y, int& max_y, int& baseline, int yPos)
254 {
255     for (InlineBox* curr = flow->firstChild(); curr; curr = curr->nextOnLine()) {
256         if (curr->isInlineFlowBox()) {
257             SVGTextPositioningElement* text = static_cast<SVGTextPositioningElement*>(curr->object()->element());
258             baseline += (int)(text->dy()->getFirst() ? text->dy()->getFirst()->value() : 0);
259             if (text->y()->getFirst()) {
260                 baseline = (int)(text->y()->getFirst()->value() - yPos);
261             }
262             placeBoxesVerticallyWithAbsBaseline(static_cast<InlineFlowBox*>(curr), heightOfBlock, min_y, max_y, baseline, yPos);
263         }
264         const Font& font = curr->object()->font(true);
265         int ascent = font.ascent();
266         int position = baseline - ascent;
267         int height = ascent + font.descent();
268         curr->setBaseline(ascent);
269         curr->setYPos(position);
270         curr->setHeight(height);
271         
272         if (position < min_y) 
273             min_y = position;
274         if (position + height > max_y) 
275             max_y = position + height;
276     }
277     if (flow->isRootInlineBox()) {
278         flow->setYPos(min_y);
279         flow->setHeight(max_y - min_y);
280         flow->setBaseline(baseline - min_y);
281         heightOfBlock += max_y - min_y;
282     }
283 }
284
285 void placeSVGFlowVertically(InlineFlowBox* flow, int& heightOfBlock)
286 {
287     int top = INT_MAX, bottom = INT_MIN, baseline = heightOfBlock;
288     placeBoxesVerticallyWithAbsBaseline(flow, heightOfBlock, top, bottom, baseline, flow->object()->yPos());
289     flow->setVerticalOverflowPositions(top, bottom);
290     flow->setVerticalSelectionPositions(top, bottom);
291 }
292
293 }
294 #endif // SVG_SUPPORT