<rdar://problem/6302405> Crash (null-deref) when using :before pseudoselector with...
[WebKit-https.git] / WebCore / rendering / SVGRootInlineBox.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  *           (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "config.h"
26
27 #if ENABLE(SVG)
28 #include "SVGRootInlineBox.h"
29
30 #include "Editor.h"
31 #include "Frame.h"
32 #include "GraphicsContext.h"
33 #include "RenderSVGRoot.h"
34 #include "SVGInlineFlowBox.h"
35 #include "SVGInlineTextBox.h"
36 #include "SVGFontElement.h"
37 #include "SVGPaintServer.h"
38 #include "SVGRenderStyleDefs.h"
39 #include "SVGRenderSupport.h"
40 #include "SVGResourceFilter.h"
41 #include "SVGTextPositioningElement.h"
42 #include "SVGURIReference.h"
43 #include "Text.h"
44 #include "UnicodeRange.h"
45
46 #include <float.h>
47
48 // Text chunk creation is complex and the whole process
49 // can easily be traced by setting this variable > 0.
50 #define DEBUG_CHUNK_BUILDING 0
51
52 namespace WebCore {
53
54 static inline bool isVerticalWritingMode(const SVGRenderStyle* style)
55 {
56     return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB; 
57 }
58
59 static inline EAlignmentBaseline dominantBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font)
60 {
61     ASSERT(text);
62
63     const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0;
64     ASSERT(style);
65
66     const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0;
67
68     EDominantBaseline baseline = style->dominantBaseline();
69     if (baseline == DB_AUTO) {
70         if (isVerticalText)
71             baseline = DB_CENTRAL;
72         else
73             baseline = DB_ALPHABETIC;
74     }
75
76     switch (baseline) {
77     case DB_USE_SCRIPT:
78         // TODO: The dominant-baseline and the baseline-table components are set by
79         //       determining the predominant script of the character data content.
80         return AB_ALPHABETIC;
81     case DB_NO_CHANGE:
82     {
83         if (parentStyle)
84             return dominantBaselineToShift(isVerticalText, text->parent(), font);
85
86         ASSERT_NOT_REACHED();
87         return AB_AUTO;
88     }
89     case DB_RESET_SIZE:
90     {
91         if (parentStyle)
92             return dominantBaselineToShift(isVerticalText, text->parent(), font);
93
94         ASSERT_NOT_REACHED();
95         return AB_AUTO;    
96     }
97     case DB_IDEOGRAPHIC:
98         return AB_IDEOGRAPHIC;
99     case DB_ALPHABETIC:
100         return AB_ALPHABETIC;
101     case DB_HANGING:
102         return AB_HANGING;
103     case DB_MATHEMATICAL:
104         return AB_MATHEMATICAL;
105     case DB_CENTRAL:
106         return AB_CENTRAL;
107     case DB_MIDDLE:
108         return AB_MIDDLE;
109     case DB_TEXT_AFTER_EDGE:
110         return AB_TEXT_AFTER_EDGE;
111     case DB_TEXT_BEFORE_EDGE:
112         return AB_TEXT_BEFORE_EDGE;
113     default:
114         ASSERT_NOT_REACHED();
115         return AB_AUTO;
116     }
117 }
118
119 static inline float alignmentBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font)
120 {
121     ASSERT(text);
122
123     const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0;
124     ASSERT(style);
125
126     const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0;
127
128     EAlignmentBaseline baseline = style->alignmentBaseline();
129     if (baseline == AB_AUTO) {
130         if (parentStyle && style->dominantBaseline() == DB_AUTO)
131             baseline = dominantBaselineToShift(isVerticalText, text->parent(), font);
132         else
133             baseline = dominantBaselineToShift(isVerticalText, text, font);
134
135         ASSERT(baseline != AB_AUTO);    
136     }
137
138     // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
139     switch (baseline) {
140     case AB_BASELINE:
141     {
142         if (parentStyle)
143             return dominantBaselineToShift(isVerticalText, text->parent(), font);
144
145         return 0.0f;
146     }
147     case AB_BEFORE_EDGE:
148     case AB_TEXT_BEFORE_EDGE:
149         return font.ascent();
150     case AB_MIDDLE:
151         return font.xHeight() / 2.0f;
152     case AB_CENTRAL:
153         // Not needed, we're taking this into account already for vertical text!
154         // return (font.ascent() - font.descent()) / 2.0f;
155         return 0.0f;
156     case AB_AFTER_EDGE:
157     case AB_TEXT_AFTER_EDGE:
158     case AB_IDEOGRAPHIC:
159         return font.descent();
160     case AB_ALPHABETIC:
161         return 0.0f;
162     case AB_HANGING:
163         return font.ascent() * 8.0f / 10.0f;
164     case AB_MATHEMATICAL:
165         return font.ascent() / 2.0f;
166     default:
167         ASSERT_NOT_REACHED();
168         return 0.0f;
169     }
170 }
171
172 static inline float glyphOrientationToAngle(const SVGRenderStyle* svgStyle, bool isVerticalText, const UChar& character)
173 {
174     switch (isVerticalText ? svgStyle->glyphOrientationVertical() : svgStyle->glyphOrientationHorizontal()) {
175     case GO_AUTO:
176     {
177         // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees.
178         //       Text which is not fullwidth will be set with a glyph-orientation of 90-degrees.
179         unsigned int unicodeRange = findCharUnicodeRange(character);
180         if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic)
181             return 90.0f;
182
183         return 0.0f;
184     }
185     case GO_90DEG:
186         return 90.0f;
187     case GO_180DEG:
188         return 180.0f;
189     case GO_270DEG:
190         return 270.0f;
191     case GO_0DEG:
192     default:
193         return 0.0f;
194     }
195 }
196
197 static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)
198 {
199     return fabsf(fmodf(orientationAngle, 180.0f)) == 0.0f;
200 }
201
202 static inline float calculateGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float orientationAngle, float glyphWidth, float glyphHeight, const Font& font, SVGChar& svgChar, float& xOrientationShift, float& yOrientationShift)
203 {
204     bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(orientationAngle);
205
206     // The function is based on spec requirements:
207     //
208     // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of
209     // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph.
210     //
211     // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of
212     // 180 degrees,then the current text position is incremented according to the horizontal metrics of the glyph.
213
214     // vertical orientation handling
215     if (isVerticalText) {
216         if (orientationAngle == 0.0f) {
217             xOrientationShift = -glyphWidth / 2.0f;
218             yOrientationShift = font.ascent();
219         } else if (orientationAngle == 90.0f) {
220             xOrientationShift = -glyphHeight;
221             yOrientationShift = font.descent();
222             svgChar.orientationShiftY = -font.ascent();
223         } else if (orientationAngle == 270.0f) {
224             xOrientationShift = glyphHeight;
225             yOrientationShift = font.descent();
226             svgChar.orientationShiftX = -glyphWidth;
227             svgChar.orientationShiftY = -font.ascent();
228         } else if (orientationAngle == 180.0f) {
229             yOrientationShift = font.ascent();
230             svgChar.orientationShiftX = -glyphWidth / 2.0f;
231             svgChar.orientationShiftY = font.ascent() - font.descent();
232         }
233
234         // vertical advance calculation
235         if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees)
236             return glyphWidth;
237
238         return glyphHeight; 
239     }
240
241     // horizontal orientation handling
242     if (orientationAngle == 90.0f) {
243         xOrientationShift = glyphWidth / 2.0f;
244         yOrientationShift = -font.descent();
245         svgChar.orientationShiftX = -glyphWidth / 2.0f - font.descent(); 
246         svgChar.orientationShiftY = font.descent();
247     } else if (orientationAngle == 270.0f) {
248         xOrientationShift = -glyphWidth / 2.0f;
249         yOrientationShift = -font.descent();
250         svgChar.orientationShiftX = -glyphWidth / 2.0f + font.descent();
251         svgChar.orientationShiftY = glyphHeight;
252     } else if (orientationAngle == 180.0f) {
253         xOrientationShift = glyphWidth / 2.0f;
254         svgChar.orientationShiftX = -glyphWidth / 2.0f;
255         svgChar.orientationShiftY = font.ascent() - font.descent();
256     }
257
258     // horizontal advance calculation
259     if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees)
260         return glyphHeight;
261
262     return glyphWidth;
263 }
264
265 static inline void startTextChunk(SVGTextChunkLayoutInfo& info)
266 {
267     info.chunk.boxes.clear();
268     info.chunk.boxes.append(SVGInlineBoxCharacterRange());
269
270     info.chunk.start = info.it;
271     info.assignChunkProperties = true;
272 }
273
274 static inline void closeTextChunk(SVGTextChunkLayoutInfo& info)
275 {
276     ASSERT(!info.chunk.boxes.last().isOpen());
277     ASSERT(info.chunk.boxes.last().isClosed());
278
279     info.chunk.end = info.it;
280     ASSERT(info.chunk.end >= info.chunk.start);
281
282     info.svgTextChunks.append(info.chunk);
283 }
284
285 RenderSVGRoot* findSVGRootObject(RenderObject* start)
286 {
287     // Find associated root inline box
288     while (start && !start->isSVGRoot())
289         start = start->parent();
290
291     ASSERT(start);
292     ASSERT(start->isSVGRoot());
293
294     return static_cast<RenderSVGRoot*>(start);
295 }
296
297 static inline FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>& chars)
298 {
299     return topLeftPositionOfCharacterRange(chars.begin(), chars.end());
300 }
301
302 FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator it, Vector<SVGChar>::iterator end)
303 {
304     float lowX = FLT_MAX, lowY = FLT_MAX;
305     for (; it != end; ++it) {
306         if (it->isHidden())
307             continue;
308
309         float x = (*it).x;
310         float y = (*it).y;
311
312         if (x < lowX)
313             lowX = x;
314
315         if (y < lowY)
316             lowY = y;
317     }
318
319     return FloatPoint(lowX, lowY);
320 }
321
322 // Helper function
323 static float calculateKerning(RenderObject* item)
324 {
325     const Font& font = item->style()->font();
326     const SVGRenderStyle* svgStyle = item->style()->svgStyle();
327
328     float kerning = 0.0f;
329     if (CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->kerning())) {
330         kerning = primitive->getFloatValue();
331
332         if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE && font.pixelSize() > 0)
333             kerning = kerning / 100.0f * font.pixelSize();
334     }
335
336     return kerning;
337 }
338
339 // Helper class for paint()
340 struct SVGRootInlineBoxPaintWalker {
341     SVGRootInlineBoxPaintWalker(SVGRootInlineBox* rootBox, SVGResourceFilter* rootFilter, RenderObject::PaintInfo paintInfo, int tx, int ty)
342         : m_rootBox(rootBox)
343         , m_chunkStarted(false)
344         , m_paintInfo(paintInfo)
345         , m_savedInfo(paintInfo)
346         , m_boundingBox(tx + rootBox->xPos(), ty + rootBox->yPos(), rootBox->width(), rootBox->height())
347         , m_filter(0)
348         , m_rootFilter(rootFilter)
349         , m_fillPaintServer(0)
350         , m_strokePaintServer(0)
351         , m_fillPaintServerObject(0)
352         , m_strokePaintServerObject(0)
353         , m_tx(tx)
354         , m_ty(ty)
355     {
356     }
357
358     ~SVGRootInlineBoxPaintWalker()
359     {
360         ASSERT(!m_filter);
361         ASSERT(!m_fillPaintServer);
362         ASSERT(!m_fillPaintServerObject);
363         ASSERT(!m_strokePaintServer);
364         ASSERT(!m_strokePaintServerObject);
365         ASSERT(!m_chunkStarted);
366     }
367
368     void teardownFillPaintServer()
369     {
370         if (!m_fillPaintServer)
371             return;
372
373         m_fillPaintServer->teardown(m_paintInfo.context, m_fillPaintServerObject, ApplyToFillTargetType, true);
374
375         m_fillPaintServer = 0;
376         m_fillPaintServerObject = 0;
377     }
378
379     void teardownStrokePaintServer()
380     {
381         if (!m_strokePaintServer)
382             return;
383
384         m_strokePaintServer->teardown(m_paintInfo.context, m_strokePaintServerObject, ApplyToStrokeTargetType, true);
385
386         m_strokePaintServer = 0;
387         m_strokePaintServerObject = 0;
388     }
389
390     void chunkStartCallback(InlineBox* box)
391     {
392         ASSERT(!m_chunkStarted);
393         m_chunkStarted = true;
394
395         InlineFlowBox* flowBox = box->parent();
396
397         // Initialize text rendering
398         RenderObject* object = flowBox->object();
399         ASSERT(object);
400
401         m_savedInfo = m_paintInfo;
402         m_paintInfo.context->save();
403
404         if (!flowBox->isRootInlineBox())
405             m_paintInfo.context->concatCTM(m_rootBox->object()->localTransform());
406
407         m_paintInfo.context->concatCTM(object->localTransform());
408
409         if (!flowBox->isRootInlineBox()) {
410             prepareToRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_rootFilter);
411             m_paintInfo.rect = object->localTransform().inverse().mapRect(m_paintInfo.rect);
412         }
413     }
414
415     void chunkEndCallback(InlineBox* box)
416     {
417         ASSERT(m_chunkStarted);
418         m_chunkStarted = false;
419
420         InlineFlowBox* flowBox = box->parent();
421
422         RenderObject* object = flowBox->object();
423         ASSERT(object);
424
425         // Clean up last used paint server
426         teardownFillPaintServer();
427         teardownStrokePaintServer();
428
429         // Finalize text rendering 
430         if (!flowBox->isRootInlineBox()) {
431             finishRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_savedInfo.context);
432             m_filter = 0;
433         }
434
435         // Restore context & repaint rect
436         m_paintInfo.context->restore();
437         m_paintInfo.rect = m_savedInfo.rect;
438     }
439
440     bool chunkSetupFillCallback(InlineBox* box)
441     {
442         InlineFlowBox* flowBox = box->parent();
443
444         // Setup fill paint server
445         RenderObject* object = flowBox->object();
446         ASSERT(object);
447
448         ASSERT(!m_strokePaintServer);
449         teardownFillPaintServer();
450
451         m_fillPaintServer = SVGPaintServer::fillPaintServer(object->style(), object);
452         if (m_fillPaintServer) {
453             m_fillPaintServer->setup(m_paintInfo.context, object, ApplyToFillTargetType, true);
454             m_fillPaintServerObject = object;
455             return true;
456         }
457
458         return false;
459     }
460
461     bool chunkSetupStrokeCallback(InlineBox* box)
462     {
463         InlineFlowBox* flowBox = box->parent();
464
465         // Setup stroke paint server
466         RenderObject* object = flowBox->object();
467         ASSERT(object);
468
469         // If we're both stroked & filled, teardown fill paint server before stroking.
470         teardownFillPaintServer();
471         teardownStrokePaintServer();
472
473         m_strokePaintServer = SVGPaintServer::strokePaintServer(object->style(), object);
474
475         if (m_strokePaintServer) {
476             m_strokePaintServer->setup(m_paintInfo.context, object, ApplyToStrokeTargetType, true);
477             m_strokePaintServerObject = object;
478             return true;
479         }
480
481         return false;
482     }
483
484     void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm,
485                               const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end)
486     {
487         RenderText* text = textBox->textObject();
488         ASSERT(text);
489
490         RenderStyle* styleToUse = text->style(textBox->isFirstLineStyle());
491         ASSERT(styleToUse);
492
493         startOffset += textBox->start();
494
495         int textDecorations = styleToUse->textDecorationsInEffect(); 
496
497         int textWidth = 0;
498         IntPoint decorationOrigin;
499         SVGTextDecorationInfo info;
500
501         if (!chunkCtm.isIdentity())
502             m_paintInfo.context->concatCTM(chunkCtm);
503
504         for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
505             if (it->isHidden())
506                 continue;
507
508             // Determine how many characters - starting from the current - can be drawn at once.
509             Vector<SVGChar>::iterator itSearch = it + 1;
510             while (itSearch != end) {
511                 if (itSearch->drawnSeperated || itSearch->isHidden())
512                     break;
513
514                 itSearch++;
515             }
516
517             const UChar* stringStart = text->characters() + startOffset + (it - start);
518             unsigned int stringLength = itSearch - it;
519
520             // Paint decorations, that have to be drawn before the text gets drawn
521             if (textDecorations != TDNONE && m_paintInfo.phase != PaintPhaseSelection) {
522                 textWidth = styleToUse->font().width(svgTextRunForInlineTextBox(stringStart, stringLength, styleToUse, textBox, (*it).x));
523                 decorationOrigin = IntPoint((int) (*it).x, (int) (*it).y - styleToUse->font().ascent());
524                 info = m_rootBox->retrievePaintServersForTextDecoration(text);
525             }
526
527             if (textDecorations & UNDERLINE && textWidth != 0.0f)
528                 textBox->paintDecoration(UNDERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
529
530             if (textDecorations & OVERLINE && textWidth != 0.0f)
531                 textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
532
533             // Paint text
534             SVGPaintServer* activePaintServer = m_fillPaintServer;
535             if (!activePaintServer)
536                 activePaintServer = m_strokePaintServer;
537
538             ASSERT(activePaintServer);
539             textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, activePaintServer);
540
541             // Paint decorations, that have to be drawn afterwards
542             if (textDecorations & LINE_THROUGH && textWidth != 0.0f)
543                 textBox->paintDecoration(LINE_THROUGH, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
544
545             // Skip processed characters
546             it = itSearch - 1;
547         }
548
549         if (!chunkCtm.isIdentity())
550             m_paintInfo.context->concatCTM(chunkCtm.inverse());
551     }
552
553 private:
554     SVGRootInlineBox* m_rootBox;
555     bool m_chunkStarted : 1;
556
557     RenderObject::PaintInfo m_paintInfo;
558     RenderObject::PaintInfo m_savedInfo;
559
560     FloatRect m_boundingBox;
561     SVGResourceFilter* m_filter;
562     SVGResourceFilter* m_rootFilter;
563
564     SVGPaintServer* m_fillPaintServer;
565     SVGPaintServer* m_strokePaintServer;
566
567     RenderObject* m_fillPaintServerObject;
568     RenderObject* m_strokePaintServerObject;
569
570     int m_tx;
571     int m_ty;
572 };
573
574 void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty)
575 {
576     if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground)
577         return;
578
579     RenderObject::PaintInfo savedInfo(paintInfo);
580     paintInfo.context->save();
581
582     SVGResourceFilter* filter = 0;
583     FloatRect boundingBox(tx + xPos(), ty + yPos(), width(), height());
584
585     // Initialize text rendering
586     paintInfo.context->concatCTM(object()->localTransform());
587     prepareToRenderSVGContent(object(), paintInfo, boundingBox, filter);
588     paintInfo.context->concatCTM(object()->localTransform().inverse());
589  
590     // Render text, chunk-by-chunk
591     SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty);
592     SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback,
593                                                            &SVGRootInlineBoxPaintWalker::chunkPortionCallback,
594                                                            &SVGRootInlineBoxPaintWalker::chunkStartCallback,
595                                                            &SVGRootInlineBoxPaintWalker::chunkEndCallback,
596                                                            &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback,
597                                                            &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback);
598
599     walkTextChunks(&walker);
600
601     // Finalize text rendering 
602     finishRenderSVGContent(object(), paintInfo, boundingBox, filter, savedInfo.context);
603     paintInfo.context->restore();
604 }
605
606 int SVGRootInlineBox::placeBoxesHorizontally(int, int& leftPosition, int& rightPosition, bool&)
607 {
608     // Remove any offsets caused by RTL text layout
609     leftPosition = 0;
610     rightPosition = 0;
611     return 0;
612 }
613
614 void SVGRootInlineBox::verticallyAlignBoxes(int& heightOfBlock)
615 {
616     // height is set by layoutInlineBoxes.
617     heightOfBlock = height();
618 }
619
620 float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range)
621 {
622     ASSERT(!range.isOpen());
623     ASSERT(range.isClosed());
624     ASSERT(range.box->isInlineTextBox());
625
626     InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box);
627     RenderText* text = textBox->textObject();
628     RenderStyle* style = text->style();
629
630     return style->font().floatWidth(svgTextRunForInlineTextBox(text->characters() + textBox->start() + range.startOffset, range.endOffset - range.startOffset, style, textBox, 0));
631 }
632
633 float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range)
634 {
635     ASSERT(!range.isOpen());
636     ASSERT(range.isClosed());
637     ASSERT(range.box->isInlineTextBox());
638
639     InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box);
640     RenderText* text = textBox->textObject();
641     const Font& font = text->style()->font();
642
643     return (range.endOffset - range.startOffset) * (font.ascent() + font.descent());
644 }
645
646 TextRun svgTextRunForInlineTextBox(const UChar* c, int len, RenderStyle* style, const InlineTextBox* textBox, float xPos)
647 {
648     ASSERT(textBox);
649     ASSERT(style);
650
651     TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->direction() == RTL, textBox->m_dirOverride || style->visuallyOrdered());
652
653 #if ENABLE(SVG_FONTS)
654     run.setReferencingRenderObject(textBox->textObject()->parent());
655 #endif
656
657     // We handle letter & word spacing ourselves
658     run.disableSpacing();
659     return run;
660 }
661
662 static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWidthOnly)
663 {
664     float length = 0.0f;
665     Vector<SVGChar>::iterator charIt = chunk.start;
666
667     Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin();
668     Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end();
669
670     for (; it != end; ++it) {
671         SVGInlineBoxCharacterRange& range = *it;
672
673         SVGInlineTextBox* box = static_cast<SVGInlineTextBox*>(range.box);
674         RenderStyle* style = box->object()->style();
675
676         for (int i = range.startOffset; i < range.endOffset; ++i) {
677             ASSERT(charIt <= chunk.end);
678
679             // Determine how many characters - starting from the current - can be measured at once.
680             // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width
681             // of a string is not the sum of the boundaries of all contained glyphs.
682             Vector<SVGChar>::iterator itSearch = charIt + 1;
683             Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i;
684             while (itSearch != endSearch) {
685                 // No need to check for 'isHidden()' here as this function is not called for text paths.
686                 if (itSearch->drawnSeperated)
687                     break;
688
689                 itSearch++;
690             }
691
692             unsigned int positionOffset = itSearch - charIt;
693
694             // Calculate width/height of subrange
695             SVGInlineBoxCharacterRange subRange;
696             subRange.box = range.box;
697             subRange.startOffset = i;
698             subRange.endOffset = i + positionOffset;
699
700             if (calcWidthOnly)
701                 length += cummulatedWidthOfInlineBoxCharacterRange(subRange);
702             else
703                 length += cummulatedHeightOfInlineBoxCharacterRange(subRange);
704
705             // Calculate gap between the previous & current range
706             // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account
707             // so add "40" as width, and analogous for B & C, add "20" as width.
708             if (itSearch > chunk.start && itSearch < chunk.end) {
709                 SVGChar& lastCharacter = *(itSearch - 1);
710                 SVGChar& currentCharacter = *itSearch;
711
712                 int offset = box->direction() == RTL ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1;
713
714                 // FIXME: does this need to change to handle multichar glyphs?
715                 int charsConsumed = 1;
716                 String glyphName;
717                 if (calcWidthOnly) {
718                     float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName);
719                     length += currentCharacter.x - lastCharacter.x - lastGlyphWidth;
720                 } else {
721                     float lastGlyphHeight = box->calculateGlyphHeight(style, offset, 0);
722                     length += currentCharacter.y - lastCharacter.y - lastGlyphHeight;
723                 }
724             }
725
726             // Advance processed characters
727             i += positionOffset - 1;
728             charIt = itSearch;
729         }
730     }
731
732     ASSERT(charIt == chunk.end);
733     return length;
734 }
735
736 static float cummulatedWidthOfTextChunk(SVGTextChunk& chunk)
737 {
738     return cummulatedWidthOrHeightOfTextChunk(chunk, true);
739 }
740
741 static float cummulatedHeightOfTextChunk(SVGTextChunk& chunk)
742 {
743     return cummulatedWidthOrHeightOfTextChunk(chunk, false);
744 }
745
746 static float calculateTextAnchorShiftForTextChunk(SVGTextChunk& chunk, ETextAnchor anchor)
747 {
748     float shift = 0.0f;
749
750     if (chunk.isVerticalText)
751         shift = cummulatedHeightOfTextChunk(chunk);
752     else
753         shift = cummulatedWidthOfTextChunk(chunk);
754
755     if (anchor == TA_MIDDLE)
756         shift *= -0.5f;
757     else
758         shift *= -1.0f;
759
760     return shift;
761 }
762
763 static void applyTextAnchorToTextChunk(SVGTextChunk& chunk)
764 {
765     // This method is not called for chunks containing chars aligned on a path.
766     // -> all characters are visible, no need to check for "isHidden()" anywhere.
767
768     if (chunk.anchor == TA_START)
769         return;
770
771     float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor);
772
773     // Apply correction to chunk
774     Vector<SVGChar>::iterator chunkIt = chunk.start;
775     for (; chunkIt != chunk.end; ++chunkIt) {
776         SVGChar& curChar = *chunkIt;
777
778         if (chunk.isVerticalText)
779             curChar.y += shift;
780         else
781             curChar.x += shift;
782     }
783
784     // Move inline boxes
785     Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
786     Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
787
788     for (; boxIt != boxEnd; ++boxIt) {
789         SVGInlineBoxCharacterRange& range = *boxIt;
790
791         InlineBox* curBox = range.box;
792         ASSERT(curBox->isInlineTextBox());
793         ASSERT(curBox->parent() && (curBox->parent()->isRootInlineBox() || curBox->parent()->isInlineFlowBox()));
794
795         // Move target box
796         if (chunk.isVerticalText)
797             curBox->setYPos(curBox->yPos() + static_cast<int>(shift));
798         else
799             curBox->setXPos(curBox->xPos() + static_cast<int>(shift));
800     }
801 }
802
803 static float calculateTextLengthCorrectionForTextChunk(SVGTextChunk& chunk, ELengthAdjust lengthAdjust, float& computedLength)
804 {
805     if (chunk.textLength <= 0.0f)
806         return 0.0f;
807
808     float computedWidth = cummulatedWidthOfTextChunk(chunk);
809     float computedHeight = cummulatedHeightOfTextChunk(chunk);
810
811     if ((computedWidth <= 0.0f && !chunk.isVerticalText) ||
812         (computedHeight <= 0.0f && chunk.isVerticalText))
813         return 0.0f;
814
815     if (chunk.isVerticalText)
816         computedLength = computedHeight;
817     else
818         computedLength = computedWidth;
819
820     if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
821         if (chunk.isVerticalText)
822             chunk.ctm.scale(1.0f, chunk.textLength / computedLength);
823         else
824             chunk.ctm.scale(chunk.textLength / computedLength, 1.0f);
825
826         return 0.0f;
827     }
828
829     return (chunk.textLength - computedLength) / float(chunk.end - chunk.start);
830 }
831
832 static void applyTextLengthCorrectionToTextChunk(SVGTextChunk& chunk)
833 {
834     // This method is not called for chunks containing chars aligned on a path.
835     // -> all characters are visible, no need to check for "isHidden()" anywhere.
836
837     // lengthAdjust="spacingAndGlyphs" is handled by modifying chunk.ctm
838     float computedLength = 0.0f;
839     float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, computedLength);
840
841     if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
842         SVGChar& firstChar = *(chunk.start);
843
844         // Assure we apply the chunk scaling in the right origin
845         AffineTransform newChunkCtm;
846         newChunkCtm.translate(firstChar.x, firstChar.y);
847         newChunkCtm = chunk.ctm * newChunkCtm;
848         newChunkCtm.translate(-firstChar.x, -firstChar.y);
849
850         chunk.ctm = newChunkCtm;
851     }
852
853     // Apply correction to chunk 
854     if (spacingToApply != 0.0f) {
855         Vector<SVGChar>::iterator chunkIt = chunk.start;
856         for (; chunkIt != chunk.end; ++chunkIt) {
857             SVGChar& curChar = *chunkIt;
858             curChar.drawnSeperated = true;
859
860             if (chunk.isVerticalText)
861                 curChar.y += (chunkIt - chunk.start) * spacingToApply;
862             else
863                 curChar.x += (chunkIt - chunk.start) * spacingToApply;
864         }
865     }
866 }
867
868 void SVGRootInlineBox::computePerCharacterLayoutInformation()
869 {
870     // Clean up any previous layout information
871     m_svgChars.clear();
872     m_svgTextChunks.clear();
873
874     // Build layout information for all contained render objects
875     SVGCharacterLayoutInfo info(m_svgChars);
876     buildLayoutInformation(this, info);
877
878     // Now all layout information are available for every character
879     // contained in any of our child inline/flow boxes. Build list
880     // of text chunks now, to be able to apply text-anchor shifts.
881     buildTextChunks(m_svgChars, m_svgTextChunks, this);
882
883     // Layout all text chunks
884     // text-anchor needs to be applied to individual chunks.
885     layoutTextChunks();
886
887     // Finally the top left position of our box is known.
888     // Propogate this knownledge to our RenderSVGText parent.
889     FloatPoint topLeft = topLeftPositionOfCharacterRange(m_svgChars);
890     object()->setPos((int) floorf(topLeft.x()), (int) floorf(topLeft.y()));
891
892     // Layout all InlineText/Flow boxes
893     // BEWARE: This requires the root top/left position to be set correctly before!
894     layoutInlineBoxes();
895 }
896
897 void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo& info)
898 {
899     if (start->isRootInlineBox()) {
900         ASSERT(start->object()->element()->hasTagName(SVGNames::textTag));
901
902         SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(start->object()->element());
903         ASSERT(positioningElement);
904         ASSERT(positioningElement->parentNode());
905
906         info.addLayoutInformation(positioningElement);
907     }
908
909     LastGlyphInfo lastGlyph;
910     
911     for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
912         if (curr->object()->isText())
913             buildLayoutInformationForTextBox(info, static_cast<InlineTextBox*>(curr), lastGlyph);
914         else {
915             ASSERT(curr->isInlineFlowBox());
916             InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
917
918             if (!flowBox->object()->element())
919                 continue; // Skip generated content.
920
921             bool isAnchor = flowBox->object()->element()->hasTagName(SVGNames::aTag);
922             bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
923
924             if (!isTextPath && !isAnchor) {
925                 SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(flowBox->object()->element());
926                 ASSERT(positioningElement);
927                 ASSERT(positioningElement->parentNode());
928
929                 info.addLayoutInformation(positioningElement);
930             } else if (!isAnchor) {
931                 info.setInPathLayout(true);
932
933                 // Handle text-anchor/textLength on path, which is special.
934                 SVGTextContentElement* textContent = 0;
935                 Node* node = flowBox->object()->element();
936                 if (node && node->isSVGElement())
937                     textContent = static_cast<SVGTextContentElement*>(node);
938                 ASSERT(textContent);
939
940                 ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
941                 ETextAnchor anchor = flowBox->object()->style()->svgStyle()->textAnchor();
942                 float textAnchorStartOffset = 0.0f;
943
944                 // Initialize sub-layout. We need to create text chunks from the textPath
945                 // children using our standard layout code, to be able to measure the
946                 // text length using our normal methods and not textPath specific hacks.
947                 Vector<SVGChar> tempChars;
948                 Vector<SVGTextChunk> tempChunks;
949
950                 SVGCharacterLayoutInfo tempInfo(tempChars);
951                 buildLayoutInformation(flowBox, tempInfo);
952
953                 buildTextChunks(tempChars, tempChunks, flowBox);
954
955                 Vector<SVGTextChunk>::iterator it = tempChunks.begin();
956                 Vector<SVGTextChunk>::iterator end = tempChunks.end();
957
958                 AffineTransform ctm;
959                 float computedLength = 0.0f;
960  
961                 for (; it != end; ++it) {
962                     SVGTextChunk& chunk = *it;
963
964                     // Apply text-length calculation
965                     info.pathExtraAdvance += calculateTextLengthCorrectionForTextChunk(chunk, lengthAdjust, computedLength);
966
967                     if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
968                         info.pathTextLength += computedLength;
969                         info.pathChunkLength += chunk.textLength;
970                     }
971
972                     // Calculate text-anchor start offset
973                     if (anchor == TA_START)
974                         continue;
975
976                     textAnchorStartOffset += calculateTextAnchorShiftForTextChunk(chunk, anchor);
977                 }
978
979                 info.addLayoutInformation(flowBox, textAnchorStartOffset);
980             }
981
982             float shiftxSaved = info.shiftx;
983             float shiftySaved = info.shifty;
984
985             buildLayoutInformation(flowBox, info);
986             info.processedChunk(shiftxSaved, shiftySaved);
987
988             if (isTextPath)
989                 info.setInPathLayout(false);
990         }
991     }
992 }
993
994 void SVGRootInlineBox::layoutInlineBoxes()
995 {
996     int lowX = INT_MAX;
997     int lowY = INT_MAX;
998     int highX = INT_MIN;
999     int highY = INT_MIN;
1000
1001     // Layout all child boxes
1002     Vector<SVGChar>::iterator it = m_svgChars.begin(); 
1003     layoutInlineBoxes(this, it, lowX, highX, lowY, highY);
1004     ASSERT(it == m_svgChars.end());
1005 }
1006
1007 void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& lowX, int& highX, int& lowY, int& highY)
1008 {
1009     for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
1010         RenderStyle* style = curr->object()->style();    
1011         const Font& font = style->font();
1012
1013         if (curr->object()->isText()) {
1014             SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(curr);
1015             unsigned length = textBox->len();
1016
1017             SVGChar curChar = *it; 
1018             ASSERT(it != m_svgChars.end());  
1019
1020             FloatRect stringRect;
1021             for (unsigned i = 0; i < length; ++i) {
1022                 ASSERT(it != m_svgChars.end());
1023
1024                 if (it->isHidden()) {
1025                     ++it;
1026                     continue;
1027                 }
1028
1029                 stringRect.unite(textBox->calculateGlyphBoundaries(style, textBox->start() + i, *it));
1030                 ++it;
1031             }
1032
1033             IntRect enclosedStringRect = enclosingIntRect(stringRect);
1034
1035             int minX = enclosedStringRect.x();
1036             int maxX = minX + enclosedStringRect.width();
1037
1038             int minY = enclosedStringRect.y();
1039             int maxY = minY + enclosedStringRect.height();
1040
1041             curr->setXPos(minX - object()->xPos());
1042             curr->setWidth(enclosedStringRect.width());
1043
1044             curr->setYPos(minY - object()->yPos());
1045             curr->setBaseline(font.ascent());
1046             curr->setHeight(enclosedStringRect.height());
1047
1048             if (minX < lowX)
1049                 lowX = minX;
1050
1051             if (maxX > highX)
1052                 highX = maxX;
1053
1054             if (minY < lowY)
1055                 lowY = minY;
1056
1057             if (maxY > highY)
1058                 highY = maxY;
1059         } else {
1060             ASSERT(curr->isInlineFlowBox());
1061
1062             int minX = INT_MAX;
1063             int minY = INT_MAX;
1064             int maxX = INT_MIN;
1065             int maxY = INT_MIN;
1066
1067             InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
1068             
1069             if (!flowBox->object()->element())
1070                 continue; // Skip generated content.
1071     
1072             layoutInlineBoxes(flowBox, it, minX, maxX, minY, maxY);
1073
1074             curr->setXPos(minX - object()->xPos());
1075             curr->setWidth(maxX - minX);
1076
1077             curr->setYPos(minY - object()->yPos());
1078             curr->setBaseline(font.ascent());
1079             curr->setHeight(maxY - minY);
1080
1081             if (minX < lowX)
1082                 lowX = minX;
1083
1084             if (maxX > highX)
1085                 highX = maxX;
1086
1087             if (minY < lowY)
1088                 lowY = minY;
1089
1090             if (maxY > highY)
1091                 highY = maxY;
1092         }
1093     }
1094
1095     if (start->isRootInlineBox()) {
1096         int top = lowY - object()->yPos();
1097         int bottom = highY - object()->yPos();
1098
1099         start->setXPos(lowX - object()->xPos());
1100         start->setYPos(top);
1101
1102         start->setWidth(highX - lowX);
1103         start->setHeight(highY - lowY);
1104
1105         start->setVerticalOverflowPositions(top, bottom);
1106         start->setVerticalSelectionPositions(top, bottom);
1107     }
1108 }
1109
1110 void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& info, InlineTextBox* textBox, LastGlyphInfo& lastGlyph)
1111 {
1112     RenderText* text = textBox->textObject();
1113     ASSERT(text);
1114
1115     RenderStyle* style = text->style(textBox->isFirstLineStyle());
1116     ASSERT(style);
1117
1118     const Font& font = style->font();
1119     SVGInlineTextBox* svgTextBox = static_cast<SVGInlineTextBox*>(textBox);
1120
1121     unsigned length = textBox->len();
1122
1123     const SVGRenderStyle* svgStyle = style->svgStyle();
1124     bool isVerticalText = isVerticalWritingMode(svgStyle);
1125
1126     int charsConsumed = 0;
1127     for (unsigned i = 0; i < length; i += charsConsumed) {
1128         SVGChar svgChar;
1129
1130         if (info.inPathLayout())
1131             svgChar.pathData = SVGCharOnPath::create();
1132
1133         float glyphWidth = 0.0f;
1134         float glyphHeight = 0.0f;
1135
1136         int extraCharsAvailable = length - i - 1;
1137
1138         String unicodeStr;
1139         String glyphName;
1140         if (textBox->direction() == RTL) {
1141             glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName);
1142             glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable);
1143             unicodeStr = String(textBox->textObject()->text()->characters() + textBox->end() - i, charsConsumed);
1144         } else {
1145             glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed, glyphName);
1146             glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->start() + i, extraCharsAvailable);
1147             unicodeStr = String(textBox->textObject()->text()->characters() + textBox->start() + i, charsConsumed);
1148         }
1149
1150         bool assignedX = false;
1151         bool assignedY = false;
1152
1153         if (info.xValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && !isVerticalText))) {
1154             if (!isVerticalText)
1155                 svgChar.newTextChunk = true;
1156
1157             assignedX = true;
1158             svgChar.drawnSeperated = true;
1159             info.curx = info.xValueNext();
1160         }
1161
1162         if (info.yValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && isVerticalText))) {
1163             if (isVerticalText)
1164                 svgChar.newTextChunk = true;
1165
1166             assignedY = true;
1167             svgChar.drawnSeperated = true;
1168             info.cury = info.yValueNext();
1169         }
1170
1171         float dx = 0.0f;
1172         float dy = 0.0f;
1173
1174         // Apply x-axis shift
1175         if (info.dxValueAvailable()) {
1176             svgChar.drawnSeperated = true;
1177
1178             dx = info.dxValueNext();
1179             info.dx += dx;
1180
1181             if (!info.inPathLayout())
1182                 info.curx += dx;
1183         }
1184
1185         // Apply y-axis shift
1186         if (info.dyValueAvailable()) {
1187             svgChar.drawnSeperated = true;
1188
1189             dy = info.dyValueNext();
1190             info.dy += dy;
1191
1192             if (!info.inPathLayout())
1193                 info.cury += dy;
1194         }
1195
1196         // Take letter & word spacing and kerning into account
1197         float spacing = font.letterSpacing() + calculateKerning(textBox->object()->element()->renderer());
1198
1199         const UChar* currentCharacter = text->characters() + (textBox->direction() == RTL ? textBox->end() - i : textBox->start() + i);
1200         const UChar* lastCharacter = 0;
1201
1202         if (textBox->direction() == RTL) {
1203             if (i < textBox->end())
1204                 lastCharacter = text->characters() + textBox->end() - i +  1;
1205         } else {
1206             if (i > 0)
1207                 lastCharacter = text->characters() + textBox->start() + i - 1;
1208         }
1209
1210         if (info.nextDrawnSeperated || spacing != 0.0f) {
1211             info.nextDrawnSeperated = false;
1212             svgChar.drawnSeperated = true;
1213         }
1214
1215         if (currentCharacter && Font::treatAsSpace(*currentCharacter) && lastCharacter && !Font::treatAsSpace(*lastCharacter)) {
1216             spacing += font.wordSpacing();
1217
1218             if (spacing != 0.0f && !info.inPathLayout())
1219                 info.nextDrawnSeperated = true;
1220         }
1221
1222         float orientationAngle = glyphOrientationToAngle(svgStyle, isVerticalText, *currentCharacter);
1223
1224         float xOrientationShift = 0.0f;
1225         float yOrientationShift = 0.0f;
1226         float glyphAdvance = calculateGlyphAdvanceAndShiftRespectingOrientation(isVerticalText, orientationAngle, glyphWidth, glyphHeight,
1227                                                                                 font, svgChar, xOrientationShift, yOrientationShift);
1228
1229         // Handle textPath layout mode
1230         if (info.inPathLayout()) {
1231             float extraAdvance = isVerticalText ? dy : dx;
1232             float newOffset = FLT_MIN;
1233
1234             if (assignedX && !isVerticalText)
1235                 newOffset = info.curx;
1236             else if (assignedY && isVerticalText)
1237                 newOffset = info.cury;
1238
1239             float correctedGlyphAdvance = glyphAdvance;
1240
1241             // Handle lengthAdjust="spacingAndGlyphs" by specifying per-character scale operations
1242             if (info.pathTextLength > 0.0f && info.pathChunkLength > 0.0f) { 
1243                 if (isVerticalText) {
1244                     svgChar.pathData->yScale = info.pathChunkLength / info.pathTextLength;
1245                     spacing *= svgChar.pathData->yScale;
1246                     correctedGlyphAdvance *= svgChar.pathData->yScale;
1247                 } else {
1248                     svgChar.pathData->xScale = info.pathChunkLength / info.pathTextLength;
1249                     spacing *= svgChar.pathData->xScale;
1250                     correctedGlyphAdvance *= svgChar.pathData->xScale;
1251                 }
1252             }
1253
1254             // Handle letter & word spacing on text path
1255             float pathExtraAdvance = info.pathExtraAdvance;
1256             info.pathExtraAdvance += spacing;
1257
1258             svgChar.pathData->hidden = !info.nextPathLayoutPointAndAngle(correctedGlyphAdvance, extraAdvance, newOffset);
1259             svgChar.drawnSeperated = true;
1260
1261             info.pathExtraAdvance = pathExtraAdvance;
1262         }
1263
1264         // Apply rotation
1265         if (info.angleValueAvailable())
1266             info.angle = info.angleValueNext();
1267
1268         // Apply baseline-shift
1269         if (info.baselineShiftValueAvailable()) {
1270             svgChar.drawnSeperated = true;
1271             float shift = info.baselineShiftValueNext();
1272
1273             if (isVerticalText)
1274                 info.shiftx += shift;
1275             else
1276                 info.shifty -= shift;
1277         }
1278
1279         // Take dominant-baseline / alignment-baseline into account
1280         yOrientationShift += alignmentBaselineToShift(isVerticalText, text, font);
1281
1282         svgChar.x = info.curx;
1283         svgChar.y = info.cury;
1284         svgChar.angle = info.angle;
1285
1286         // For text paths any shift (dx/dy/baseline-shift) has to be applied after the rotation
1287         if (!info.inPathLayout()) {
1288             svgChar.x += info.shiftx + xOrientationShift;
1289             svgChar.y += info.shifty + yOrientationShift;
1290
1291             if (orientationAngle != 0.0f)
1292                 svgChar.angle += orientationAngle;
1293
1294             if (svgChar.angle != 0.0f)
1295                 svgChar.drawnSeperated = true;
1296         } else {
1297             svgChar.pathData->orientationAngle = orientationAngle;
1298
1299             if (isVerticalText)
1300                 svgChar.angle -= 90.0f;
1301
1302             svgChar.pathData->xShift = info.shiftx + xOrientationShift;
1303             svgChar.pathData->yShift = info.shifty + yOrientationShift;
1304
1305             // Translate to glyph midpoint
1306             if (isVerticalText) {
1307                 svgChar.pathData->xShift += info.dx;
1308                 svgChar.pathData->yShift -= glyphAdvance / 2.0f;
1309             } else {
1310                 svgChar.pathData->xShift -= glyphAdvance / 2.0f;
1311                 svgChar.pathData->yShift += info.dy;
1312             }
1313         }
1314
1315         double kerning = 0.0;
1316 #if ENABLE(SVG_FONTS)
1317         SVGFontElement* svgFont = 0;
1318         if (style->font().isSVGFont())
1319             svgFont = style->font().svgFont();
1320
1321         if (lastGlyph.isValid && style->font().isSVGFont()) {
1322             SVGHorizontalKerningPair kerningPair;
1323             if (svgFont->getHorizontalKerningPairForStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeStr, glyphName, kerningPair))
1324                 kerning = kerningPair.kerning;
1325         }
1326
1327         if (style->font().isSVGFont()) {
1328             lastGlyph.unicode = unicodeStr;
1329             lastGlyph.glyphName = glyphName;
1330             lastGlyph.isValid = true;
1331         } else
1332             lastGlyph.isValid = false;
1333 #endif
1334
1335         svgChar.x -= (float)kerning;
1336
1337         // Advance to new position
1338         if (isVerticalText) {
1339             svgChar.drawnSeperated = true;
1340             info.cury += glyphAdvance + spacing;
1341         } else
1342             info.curx += glyphAdvance + spacing - (float)kerning;
1343
1344         // Advance to next character group
1345         for (int k = 0; k < charsConsumed; ++k) {
1346             info.svgChars.append(svgChar);
1347             info.processedSingleCharacter();
1348             svgChar.drawnSeperated = false;
1349             svgChar.newTextChunk = false;
1350         }
1351     }
1352 }
1353
1354 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, Vector<SVGTextChunk>& svgTextChunks, InlineFlowBox* start)
1355 {
1356     SVGTextChunkLayoutInfo info(svgTextChunks);
1357     info.it = svgChars.begin();
1358     info.chunk.start = svgChars.begin();
1359     info.chunk.end = svgChars.begin();
1360
1361     buildTextChunks(svgChars, start, info);
1362     ASSERT(info.it == svgChars.end());
1363 }
1364
1365 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* start, SVGTextChunkLayoutInfo& info)
1366 {
1367 #if DEBUG_CHUNK_BUILDING > 1
1368     fprintf(stderr, " -> buildTextChunks(start=%p)\n", start);
1369 #endif
1370
1371     for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
1372         if (curr->object()->isText()) {
1373             InlineTextBox* textBox = static_cast<InlineTextBox*>(curr);
1374
1375             unsigned length = textBox->len();
1376             if (!length)
1377                 continue;
1378
1379 #if DEBUG_CHUNK_BUILDING > 1
1380             fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n",
1381                             textBox, length, textBox->start(), textBox->end(), (int) info.handlingTextPath);
1382 #endif
1383
1384             RenderText* text = textBox->textObject();
1385             ASSERT(text);
1386             ASSERT(text->element());
1387
1388             SVGTextContentElement* textContent = 0;
1389             Node* node = text->element()->parent();
1390             while (node && node->isSVGElement() && !textContent) {
1391                 if (static_cast<SVGElement*>(node)->isTextContent())
1392                     textContent = static_cast<SVGTextContentElement*>(node);
1393                 else
1394                     node = node->parentNode();
1395             }
1396             ASSERT(textContent);
1397
1398             // Start new character range for the first chunk
1399             bool isFirstCharacter = info.svgTextChunks.isEmpty() && info.chunk.start == info.it && info.chunk.start == info.chunk.end;
1400             if (isFirstCharacter) {
1401                 ASSERT(info.chunk.boxes.isEmpty());
1402                 info.chunk.boxes.append(SVGInlineBoxCharacterRange());
1403             } else
1404                 ASSERT(!info.chunk.boxes.isEmpty());
1405
1406             // Walk string to find out new chunk positions, if existant
1407             for (unsigned i = 0; i < length; ++i) {
1408                 ASSERT(info.it != svgChars.end());
1409
1410                 SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
1411                 if (range.isOpen()) {
1412                     range.box = curr;
1413                     range.startOffset = (i == 0 ? 0 : i - 1);
1414
1415 #if DEBUG_CHUNK_BUILDING > 1
1416                     fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset);
1417 #endif
1418                 }
1419
1420                 // If a new (or the first) chunk has been started, record it's text-anchor and writing mode.
1421                 if (info.assignChunkProperties) {
1422                     info.assignChunkProperties = false;
1423
1424                     info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
1425                     info.chunk.isTextPath = info.handlingTextPath;
1426                     info.chunk.anchor = text->style()->svgStyle()->textAnchor();
1427                     info.chunk.textLength = textContent->textLength().value(textContent);
1428                     info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
1429
1430 #if DEBUG_CHUNK_BUILDING > 1
1431                     fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", info.chunk.isVerticalText, info.chunk.anchor);
1432 #endif
1433                 }
1434
1435                 if (i > 0 && !isFirstCharacter && (*info.it).newTextChunk) {
1436                     // Close mid chunk & character range
1437                     ASSERT(!range.isOpen());
1438                     ASSERT(!range.isClosed());
1439
1440                     range.endOffset = i;
1441                     closeTextChunk(info);
1442
1443 #if DEBUG_CHUNK_BUILDING > 1
1444                     fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset);
1445 #endif
1446     
1447                     // Prepare for next chunk, if we're not at the end
1448                     startTextChunk(info);
1449                     if (i + 1 == length) {
1450 #if DEBUG_CHUNK_BUILDING > 1
1451                         fprintf(stderr, " | -> Record last chunk of inline text box!\n");
1452 #endif
1453
1454                         startTextChunk(info);
1455                         SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
1456
1457                         info.assignChunkProperties = false;
1458                         info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
1459                         info.chunk.isTextPath = info.handlingTextPath;
1460                         info.chunk.anchor = text->style()->svgStyle()->textAnchor();
1461                         info.chunk.textLength = textContent->textLength().value(textContent);
1462                         info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
1463
1464                         range.box = curr;
1465                         range.startOffset = i;
1466
1467                         ASSERT(!range.isOpen());
1468                         ASSERT(!range.isClosed());
1469                     }
1470                 }
1471
1472                 // This should only hold true for the first character of the first chunk
1473                 if (isFirstCharacter)
1474                     isFirstCharacter = false;
1475     
1476                 ++info.it;
1477             }
1478
1479 #if DEBUG_CHUNK_BUILDING > 1    
1480             fprintf(stderr, " -> Finished inline text box!\n");
1481 #endif
1482
1483             SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
1484             if (!range.isOpen() && !range.isClosed()) {
1485 #if DEBUG_CHUNK_BUILDING > 1
1486                 fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length);
1487 #endif
1488
1489                 // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk.
1490                 range.endOffset = length;
1491
1492                 if (info.it != svgChars.end()) {
1493 #if DEBUG_CHUNK_BUILDING > 1
1494                     fprintf(stderr, " -> Not at last character yet!\n");
1495 #endif
1496
1497                     // If we're not at the end of the last box to be processed, and if the next
1498                     // character starts a new chunk, then close the current chunk and start a new one.
1499                     if ((*info.it).newTextChunk) {
1500 #if DEBUG_CHUNK_BUILDING > 1
1501                         fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n");
1502 #endif
1503
1504                         closeTextChunk(info);
1505                         startTextChunk(info);
1506                     } else {
1507                         // Just start a new character range
1508                         info.chunk.boxes.append(SVGInlineBoxCharacterRange());
1509
1510 #if DEBUG_CHUNK_BUILDING > 1
1511                         fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n");
1512 #endif
1513                     }
1514                 } else {
1515 #if DEBUG_CHUNK_BUILDING > 1
1516                     fprintf(stderr, " -> Closing final chunk! Finished processing!\n");
1517 #endif
1518
1519                     // Close final chunk, once we're at the end of the last box
1520                     closeTextChunk(info);
1521                 }
1522             }
1523         } else {
1524             ASSERT(curr->isInlineFlowBox());
1525             InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
1526
1527             if (!flowBox->object()->element())
1528                 continue; // Skip generated content.
1529
1530             bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
1531
1532 #if DEBUG_CHUNK_BUILDING > 1
1533             fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath);
1534 #endif
1535
1536             if (isTextPath)
1537                 info.handlingTextPath = true;
1538
1539             buildTextChunks(svgChars, flowBox, info);
1540
1541             if (isTextPath)
1542                 info.handlingTextPath = false;
1543         }
1544     }
1545
1546 #if DEBUG_CHUNK_BUILDING > 1
1547     fprintf(stderr, " <- buildTextChunks(start=%p)\n", start);
1548 #endif
1549 }
1550
1551 const Vector<SVGTextChunk>& SVGRootInlineBox::svgTextChunks() const 
1552 {
1553     return m_svgTextChunks;
1554 }
1555
1556 void SVGRootInlineBox::layoutTextChunks()
1557 {
1558     Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
1559     Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end();
1560
1561     for (; it != end; ++it) {
1562         SVGTextChunk& chunk = *it;
1563
1564 #if DEBUG_CHUNK_BUILDING > 0
1565         {
1566             fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n",
1567                     (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText,
1568                     (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int) (chunk.end - chunk.start));
1569
1570             Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
1571             Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
1572
1573             unsigned int i = 0;
1574             for (; boxIt != boxEnd; ++boxIt) {
1575                 SVGInlineBoxCharacterRange& range = *boxIt; i++;
1576                 fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box);
1577             }
1578         }
1579 #endif
1580
1581         if (chunk.isTextPath)
1582             continue;
1583
1584         // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts.
1585         applyTextLengthCorrectionToTextChunk(chunk);
1586  
1587         // text-anchor is already handled for textPath layouts.
1588         applyTextAnchorToTextChunk(chunk);
1589     }
1590 }
1591
1592 static inline void addPaintServerToTextDecorationInfo(ETextDecoration decoration, SVGTextDecorationInfo& info, RenderObject* object)
1593 {
1594     if (object->style()->svgStyle()->hasFill())
1595         info.fillServerMap.set(decoration, object);
1596
1597     if (object->style()->svgStyle()->hasStroke())
1598         info.strokeServerMap.set(decoration, object);
1599 }
1600
1601 SVGTextDecorationInfo SVGRootInlineBox::retrievePaintServersForTextDecoration(RenderObject* start)
1602 {
1603     ASSERT(start);
1604
1605     Vector<RenderObject*> parentChain;
1606     while ((start = start->parent())) {
1607         parentChain.prepend(start);
1608
1609         // Stop at our direct <text> parent.
1610         if (start->isSVGText())
1611             break;
1612     }
1613
1614     Vector<RenderObject*>::iterator it = parentChain.begin();
1615     Vector<RenderObject*>::iterator end = parentChain.end();
1616
1617     SVGTextDecorationInfo info;
1618
1619     for (; it != end; ++it) {
1620         RenderObject* object = *it;
1621         ASSERT(object);
1622
1623         RenderStyle* style = object->style();
1624         ASSERT(style);
1625
1626         int decorations = style->textDecoration();
1627         if (decorations != NONE) {
1628             if (decorations & OVERLINE)
1629                 addPaintServerToTextDecorationInfo(OVERLINE, info, object);
1630
1631             if (decorations & UNDERLINE)
1632                 addPaintServerToTextDecorationInfo(UNDERLINE, info, object);
1633
1634             if (decorations & LINE_THROUGH)
1635                 addPaintServerToTextDecorationInfo(LINE_THROUGH, info, object);
1636         }
1637     }
1638
1639     return info;
1640 }
1641
1642 void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase* walker, const SVGInlineTextBox* textBox)
1643 {
1644     ASSERT(walker);
1645
1646     Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
1647     Vector<SVGTextChunk>::iterator itEnd = m_svgTextChunks.end();
1648
1649     for (; it != itEnd; ++it) {
1650         SVGTextChunk& curChunk = *it;
1651
1652         Vector<SVGInlineBoxCharacterRange>::iterator boxIt = curChunk.boxes.begin();
1653         Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = curChunk.boxes.end();
1654
1655         InlineBox* lastNotifiedBox = 0;
1656         InlineBox* prevBox = 0;
1657
1658         unsigned int chunkOffset = 0;
1659         bool startedFirstChunk = false;
1660
1661         for (; boxIt != boxEnd; ++boxIt) {
1662             SVGInlineBoxCharacterRange& range = *boxIt;
1663
1664             ASSERT(range.box->isInlineTextBox());
1665             SVGInlineTextBox* rangeTextBox = static_cast<SVGInlineTextBox*>(range.box);
1666
1667             if (textBox && rangeTextBox != textBox) {
1668                 chunkOffset += range.endOffset - range.startOffset;
1669                 continue;
1670             }
1671
1672             // Eventually notify that we started a new chunk
1673             if (!textBox && !startedFirstChunk) {
1674                 startedFirstChunk = true;
1675
1676                 lastNotifiedBox = range.box;
1677                 walker->start(range.box);
1678             } else {
1679                 // Eventually apply new style, as this chunk spans multiple boxes (with possible different styling)
1680                 if (prevBox && prevBox != range.box) {
1681                     lastNotifiedBox = range.box;
1682
1683                     walker->end(prevBox);
1684                     walker->start(lastNotifiedBox);
1685                 }
1686             }
1687
1688             unsigned int length = range.endOffset - range.startOffset;
1689
1690             Vector<SVGChar>::iterator itCharBegin = curChunk.start + chunkOffset;
1691             Vector<SVGChar>::iterator itCharEnd = curChunk.start + chunkOffset + length;
1692             ASSERT(itCharEnd <= curChunk.end);
1693
1694             // Process this chunk portion
1695             if (textBox)
1696                 (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1697             else {
1698                 if (walker->setupFill(range.box))
1699                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1700
1701                 if (walker->setupStroke(range.box))
1702                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1703             }
1704
1705             chunkOffset += length;
1706
1707             if (!textBox)
1708                 prevBox = range.box;
1709         }
1710
1711         if (!textBox && startedFirstChunk)
1712             walker->end(lastNotifiedBox);
1713     }
1714 }
1715
1716 } // namespace WebCore
1717
1718 #endif // ENABLE(SVG)