e5f9fdf8c3a1a7a46dd35845d3087337e5dbf2a0
[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             bool isAnchor = flowBox->object()->element()->hasTagName(SVGNames::aTag);
919             bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
920
921             if (!isTextPath && !isAnchor) {
922                 SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(flowBox->object()->element());
923                 ASSERT(positioningElement);
924                 ASSERT(positioningElement->parentNode());
925
926                 info.addLayoutInformation(positioningElement);
927             } else if (!isAnchor) {
928                 info.setInPathLayout(true);
929
930                 // Handle text-anchor/textLength on path, which is special.
931                 SVGTextContentElement* textContent = 0;
932                 Node* node = flowBox->object()->element();
933                 if (node && node->isSVGElement())
934                     textContent = static_cast<SVGTextContentElement*>(node);
935                 ASSERT(textContent);
936
937                 ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
938                 ETextAnchor anchor = flowBox->object()->style()->svgStyle()->textAnchor();
939                 float textAnchorStartOffset = 0.0f;
940
941                 // Initialize sub-layout. We need to create text chunks from the textPath
942                 // children using our standard layout code, to be able to measure the
943                 // text length using our normal methods and not textPath specific hacks.
944                 Vector<SVGChar> tempChars;
945                 Vector<SVGTextChunk> tempChunks;
946
947                 SVGCharacterLayoutInfo tempInfo(tempChars);
948                 buildLayoutInformation(flowBox, tempInfo);
949
950                 buildTextChunks(tempChars, tempChunks, flowBox);
951
952                 Vector<SVGTextChunk>::iterator it = tempChunks.begin();
953                 Vector<SVGTextChunk>::iterator end = tempChunks.end();
954
955                 AffineTransform ctm;
956                 float computedLength = 0.0f;
957  
958                 for (; it != end; ++it) {
959                     SVGTextChunk& chunk = *it;
960
961                     // Apply text-length calculation
962                     info.pathExtraAdvance += calculateTextLengthCorrectionForTextChunk(chunk, lengthAdjust, computedLength);
963
964                     if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
965                         info.pathTextLength += computedLength;
966                         info.pathChunkLength += chunk.textLength;
967                     }
968
969                     // Calculate text-anchor start offset
970                     if (anchor == TA_START)
971                         continue;
972
973                     textAnchorStartOffset += calculateTextAnchorShiftForTextChunk(chunk, anchor);
974                 }
975
976                 info.addLayoutInformation(flowBox, textAnchorStartOffset);
977             }
978
979             float shiftxSaved = info.shiftx;
980             float shiftySaved = info.shifty;
981
982             buildLayoutInformation(flowBox, info);
983             info.processedChunk(shiftxSaved, shiftySaved);
984
985             if (isTextPath)
986                 info.setInPathLayout(false);
987         }
988     }
989 }
990
991 void SVGRootInlineBox::layoutInlineBoxes()
992 {
993     int lowX = INT_MAX;
994     int lowY = INT_MAX;
995     int highX = INT_MIN;
996     int highY = INT_MIN;
997
998     // Layout all child boxes
999     Vector<SVGChar>::iterator it = m_svgChars.begin(); 
1000     layoutInlineBoxes(this, it, lowX, highX, lowY, highY);
1001     ASSERT(it == m_svgChars.end());
1002 }
1003
1004 void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& lowX, int& highX, int& lowY, int& highY)
1005 {
1006     for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
1007         RenderStyle* style = curr->object()->style();    
1008         const Font& font = style->font();
1009
1010         if (curr->object()->isText()) {
1011             SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(curr);
1012             unsigned length = textBox->len();
1013
1014             SVGChar curChar = *it; 
1015             ASSERT(it != m_svgChars.end());  
1016
1017             FloatRect stringRect;
1018             for (unsigned i = 0; i < length; ++i) {
1019                 ASSERT(it != m_svgChars.end());
1020
1021                 if (it->isHidden()) {
1022                     ++it;
1023                     continue;
1024                 }
1025
1026                 stringRect.unite(textBox->calculateGlyphBoundaries(style, textBox->start() + i, *it));
1027                 ++it;
1028             }
1029
1030             IntRect enclosedStringRect = enclosingIntRect(stringRect);
1031
1032             int minX = enclosedStringRect.x();
1033             int maxX = minX + enclosedStringRect.width();
1034
1035             int minY = enclosedStringRect.y();
1036             int maxY = minY + enclosedStringRect.height();
1037
1038             curr->setXPos(minX - object()->xPos());
1039             curr->setWidth(enclosedStringRect.width());
1040
1041             curr->setYPos(minY - object()->yPos());
1042             curr->setBaseline(font.ascent());
1043             curr->setHeight(enclosedStringRect.height());
1044
1045             if (minX < lowX)
1046                 lowX = minX;
1047
1048             if (maxX > highX)
1049                 highX = maxX;
1050
1051             if (minY < lowY)
1052                 lowY = minY;
1053
1054             if (maxY > highY)
1055                 highY = maxY;
1056         } else {
1057             ASSERT(curr->isInlineFlowBox());
1058
1059             int minX = INT_MAX;
1060             int minY = INT_MAX;
1061             int maxX = INT_MIN;
1062             int maxY = INT_MIN;
1063
1064             InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
1065             layoutInlineBoxes(flowBox, it, minX, maxX, minY, maxY);
1066
1067             curr->setXPos(minX - object()->xPos());
1068             curr->setWidth(maxX - minX);
1069
1070             curr->setYPos(minY - object()->yPos());
1071             curr->setBaseline(font.ascent());
1072             curr->setHeight(maxY - minY);
1073
1074             if (minX < lowX)
1075                 lowX = minX;
1076
1077             if (maxX > highX)
1078                 highX = maxX;
1079
1080             if (minY < lowY)
1081                 lowY = minY;
1082
1083             if (maxY > highY)
1084                 highY = maxY;
1085         }
1086     }
1087
1088     if (start->isRootInlineBox()) {
1089         int top = lowY - object()->yPos();
1090         int bottom = highY - object()->yPos();
1091
1092         start->setXPos(lowX - object()->xPos());
1093         start->setYPos(top);
1094
1095         start->setWidth(highX - lowX);
1096         start->setHeight(highY - lowY);
1097
1098         start->setVerticalOverflowPositions(top, bottom);
1099         start->setVerticalSelectionPositions(top, bottom);
1100     }
1101 }
1102
1103 void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& info, InlineTextBox* textBox, LastGlyphInfo& lastGlyph)
1104 {
1105     RenderText* text = textBox->textObject();
1106     ASSERT(text);
1107
1108     RenderStyle* style = text->style(textBox->isFirstLineStyle());
1109     ASSERT(style);
1110
1111     const Font& font = style->font();
1112     SVGInlineTextBox* svgTextBox = static_cast<SVGInlineTextBox*>(textBox);
1113
1114     unsigned length = textBox->len();
1115
1116     const SVGRenderStyle* svgStyle = style->svgStyle();
1117     bool isVerticalText = isVerticalWritingMode(svgStyle);
1118
1119     int charsConsumed = 0;
1120     for (unsigned i = 0; i < length; i += charsConsumed) {
1121         SVGChar svgChar;
1122
1123         if (info.inPathLayout())
1124             svgChar.pathData = SVGCharOnPath::create();
1125
1126         float glyphWidth = 0.0f;
1127         float glyphHeight = 0.0f;
1128
1129         int extraCharsAvailable = length - i - 1;
1130
1131         String unicodeStr;
1132         String glyphName;
1133         if (textBox->direction() == RTL) {
1134             glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName);
1135             glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable);
1136             unicodeStr = String(textBox->textObject()->text()->characters() + textBox->end() - i, charsConsumed);
1137         } else {
1138             glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed, glyphName);
1139             glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->start() + i, extraCharsAvailable);
1140             unicodeStr = String(textBox->textObject()->text()->characters() + textBox->start() + i, charsConsumed);
1141         }
1142
1143         bool assignedX = false;
1144         bool assignedY = false;
1145
1146         if (info.xValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && !isVerticalText))) {
1147             if (!isVerticalText)
1148                 svgChar.newTextChunk = true;
1149
1150             assignedX = true;
1151             svgChar.drawnSeperated = true;
1152             info.curx = info.xValueNext();
1153         }
1154
1155         if (info.yValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && isVerticalText))) {
1156             if (isVerticalText)
1157                 svgChar.newTextChunk = true;
1158
1159             assignedY = true;
1160             svgChar.drawnSeperated = true;
1161             info.cury = info.yValueNext();
1162         }
1163
1164         float dx = 0.0f;
1165         float dy = 0.0f;
1166
1167         // Apply x-axis shift
1168         if (info.dxValueAvailable()) {
1169             svgChar.drawnSeperated = true;
1170
1171             dx = info.dxValueNext();
1172             info.dx += dx;
1173
1174             if (!info.inPathLayout())
1175                 info.curx += dx;
1176         }
1177
1178         // Apply y-axis shift
1179         if (info.dyValueAvailable()) {
1180             svgChar.drawnSeperated = true;
1181
1182             dy = info.dyValueNext();
1183             info.dy += dy;
1184
1185             if (!info.inPathLayout())
1186                 info.cury += dy;
1187         }
1188
1189         // Take letter & word spacing and kerning into account
1190         float spacing = font.letterSpacing() + calculateKerning(textBox->object()->element()->renderer());
1191
1192         const UChar* currentCharacter = text->characters() + (textBox->direction() == RTL ? textBox->end() - i : textBox->start() + i);
1193         const UChar* lastCharacter = 0;
1194
1195         if (textBox->direction() == RTL) {
1196             if (i < textBox->end())
1197                 lastCharacter = text->characters() + textBox->end() - i +  1;
1198         } else {
1199             if (i > 0)
1200                 lastCharacter = text->characters() + textBox->start() + i - 1;
1201         }
1202
1203         if (info.nextDrawnSeperated || spacing != 0.0f) {
1204             info.nextDrawnSeperated = false;
1205             svgChar.drawnSeperated = true;
1206         }
1207
1208         if (currentCharacter && Font::treatAsSpace(*currentCharacter) && lastCharacter && !Font::treatAsSpace(*lastCharacter)) {
1209             spacing += font.wordSpacing();
1210
1211             if (spacing != 0.0f && !info.inPathLayout())
1212                 info.nextDrawnSeperated = true;
1213         }
1214
1215         float orientationAngle = glyphOrientationToAngle(svgStyle, isVerticalText, *currentCharacter);
1216
1217         float xOrientationShift = 0.0f;
1218         float yOrientationShift = 0.0f;
1219         float glyphAdvance = calculateGlyphAdvanceAndShiftRespectingOrientation(isVerticalText, orientationAngle, glyphWidth, glyphHeight,
1220                                                                                 font, svgChar, xOrientationShift, yOrientationShift);
1221
1222         // Handle textPath layout mode
1223         if (info.inPathLayout()) {
1224             float extraAdvance = isVerticalText ? dy : dx;
1225             float newOffset = FLT_MIN;
1226
1227             if (assignedX && !isVerticalText)
1228                 newOffset = info.curx;
1229             else if (assignedY && isVerticalText)
1230                 newOffset = info.cury;
1231
1232             float correctedGlyphAdvance = glyphAdvance;
1233
1234             // Handle lengthAdjust="spacingAndGlyphs" by specifying per-character scale operations
1235             if (info.pathTextLength > 0.0f && info.pathChunkLength > 0.0f) { 
1236                 if (isVerticalText) {
1237                     svgChar.pathData->yScale = info.pathChunkLength / info.pathTextLength;
1238                     spacing *= svgChar.pathData->yScale;
1239                     correctedGlyphAdvance *= svgChar.pathData->yScale;
1240                 } else {
1241                     svgChar.pathData->xScale = info.pathChunkLength / info.pathTextLength;
1242                     spacing *= svgChar.pathData->xScale;
1243                     correctedGlyphAdvance *= svgChar.pathData->xScale;
1244                 }
1245             }
1246
1247             // Handle letter & word spacing on text path
1248             float pathExtraAdvance = info.pathExtraAdvance;
1249             info.pathExtraAdvance += spacing;
1250
1251             svgChar.pathData->hidden = !info.nextPathLayoutPointAndAngle(correctedGlyphAdvance, extraAdvance, newOffset);
1252             svgChar.drawnSeperated = true;
1253
1254             info.pathExtraAdvance = pathExtraAdvance;
1255         }
1256
1257         // Apply rotation
1258         if (info.angleValueAvailable())
1259             info.angle = info.angleValueNext();
1260
1261         // Apply baseline-shift
1262         if (info.baselineShiftValueAvailable()) {
1263             svgChar.drawnSeperated = true;
1264             float shift = info.baselineShiftValueNext();
1265
1266             if (isVerticalText)
1267                 info.shiftx += shift;
1268             else
1269                 info.shifty -= shift;
1270         }
1271
1272         // Take dominant-baseline / alignment-baseline into account
1273         yOrientationShift += alignmentBaselineToShift(isVerticalText, text, font);
1274
1275         svgChar.x = info.curx;
1276         svgChar.y = info.cury;
1277         svgChar.angle = info.angle;
1278
1279         // For text paths any shift (dx/dy/baseline-shift) has to be applied after the rotation
1280         if (!info.inPathLayout()) {
1281             svgChar.x += info.shiftx + xOrientationShift;
1282             svgChar.y += info.shifty + yOrientationShift;
1283
1284             if (orientationAngle != 0.0f)
1285                 svgChar.angle += orientationAngle;
1286
1287             if (svgChar.angle != 0.0f)
1288                 svgChar.drawnSeperated = true;
1289         } else {
1290             svgChar.pathData->orientationAngle = orientationAngle;
1291
1292             if (isVerticalText)
1293                 svgChar.angle -= 90.0f;
1294
1295             svgChar.pathData->xShift = info.shiftx + xOrientationShift;
1296             svgChar.pathData->yShift = info.shifty + yOrientationShift;
1297
1298             // Translate to glyph midpoint
1299             if (isVerticalText) {
1300                 svgChar.pathData->xShift += info.dx;
1301                 svgChar.pathData->yShift -= glyphAdvance / 2.0f;
1302             } else {
1303                 svgChar.pathData->xShift -= glyphAdvance / 2.0f;
1304                 svgChar.pathData->yShift += info.dy;
1305             }
1306         }
1307
1308         double kerning = 0.0;
1309 #if ENABLE(SVG_FONTS)
1310         SVGFontElement* svgFont = 0;
1311         if (style->font().isSVGFont())
1312             svgFont = style->font().svgFont();
1313
1314         if (lastGlyph.isValid && style->font().isSVGFont()) {
1315             SVGHorizontalKerningPair kerningPair;
1316             if (svgFont->getHorizontalKerningPairForStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeStr, glyphName, kerningPair))
1317                 kerning = kerningPair.kerning;
1318         }
1319
1320         if (style->font().isSVGFont()) {
1321             lastGlyph.unicode = unicodeStr;
1322             lastGlyph.glyphName = glyphName;
1323             lastGlyph.isValid = true;
1324         } else
1325             lastGlyph.isValid = false;
1326 #endif
1327
1328         svgChar.x -= (float)kerning;
1329
1330         // Advance to new position
1331         if (isVerticalText) {
1332             svgChar.drawnSeperated = true;
1333             info.cury += glyphAdvance + spacing;
1334         } else
1335             info.curx += glyphAdvance + spacing - (float)kerning;
1336
1337         // Advance to next character group
1338         for (int k = 0; k < charsConsumed; ++k) {
1339             info.svgChars.append(svgChar);
1340             info.processedSingleCharacter();
1341             svgChar.drawnSeperated = false;
1342             svgChar.newTextChunk = false;
1343         }
1344     }
1345 }
1346
1347 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, Vector<SVGTextChunk>& svgTextChunks, InlineFlowBox* start)
1348 {
1349     SVGTextChunkLayoutInfo info(svgTextChunks);
1350     info.it = svgChars.begin();
1351     info.chunk.start = svgChars.begin();
1352     info.chunk.end = svgChars.begin();
1353
1354     buildTextChunks(svgChars, start, info);
1355     ASSERT(info.it == svgChars.end());
1356 }
1357
1358 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* start, SVGTextChunkLayoutInfo& info)
1359 {
1360 #if DEBUG_CHUNK_BUILDING > 1
1361     fprintf(stderr, " -> buildTextChunks(start=%p)\n", start);
1362 #endif
1363
1364     for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
1365         if (curr->object()->isText()) {
1366             InlineTextBox* textBox = static_cast<InlineTextBox*>(curr);
1367
1368             unsigned length = textBox->len();
1369             if (!length)
1370                 continue;
1371
1372 #if DEBUG_CHUNK_BUILDING > 1
1373             fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n",
1374                             textBox, length, textBox->start(), textBox->end(), (int) info.handlingTextPath);
1375 #endif
1376
1377             RenderText* text = textBox->textObject();
1378             ASSERT(text);
1379             ASSERT(text->element());
1380
1381             SVGTextContentElement* textContent = 0;
1382             Node* node = text->element()->parent();
1383             while (node && node->isSVGElement() && !textContent) {
1384                 if (static_cast<SVGElement*>(node)->isTextContent())
1385                     textContent = static_cast<SVGTextContentElement*>(node);
1386                 else
1387                     node = node->parentNode();
1388             }
1389             ASSERT(textContent);
1390
1391             // Start new character range for the first chunk
1392             bool isFirstCharacter = info.svgTextChunks.isEmpty() && info.chunk.start == info.it && info.chunk.start == info.chunk.end;
1393             if (isFirstCharacter) {
1394                 ASSERT(info.chunk.boxes.isEmpty());
1395                 info.chunk.boxes.append(SVGInlineBoxCharacterRange());
1396             } else
1397                 ASSERT(!info.chunk.boxes.isEmpty());
1398
1399             // Walk string to find out new chunk positions, if existant
1400             for (unsigned i = 0; i < length; ++i) {
1401                 ASSERT(info.it != svgChars.end());
1402
1403                 SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
1404                 if (range.isOpen()) {
1405                     range.box = curr;
1406                     range.startOffset = (i == 0 ? 0 : i - 1);
1407
1408 #if DEBUG_CHUNK_BUILDING > 1
1409                     fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset);
1410 #endif
1411                 }
1412
1413                 // If a new (or the first) chunk has been started, record it's text-anchor and writing mode.
1414                 if (info.assignChunkProperties) {
1415                     info.assignChunkProperties = false;
1416
1417                     info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
1418                     info.chunk.isTextPath = info.handlingTextPath;
1419                     info.chunk.anchor = text->style()->svgStyle()->textAnchor();
1420                     info.chunk.textLength = textContent->textLength().value(textContent);
1421                     info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
1422
1423 #if DEBUG_CHUNK_BUILDING > 1
1424                     fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", info.chunk.isVerticalText, info.chunk.anchor);
1425 #endif
1426                 }
1427
1428                 if (i > 0 && !isFirstCharacter && (*info.it).newTextChunk) {
1429                     // Close mid chunk & character range
1430                     ASSERT(!range.isOpen());
1431                     ASSERT(!range.isClosed());
1432
1433                     range.endOffset = i;
1434                     closeTextChunk(info);
1435
1436 #if DEBUG_CHUNK_BUILDING > 1
1437                     fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset);
1438 #endif
1439     
1440                     // Prepare for next chunk, if we're not at the end
1441                     startTextChunk(info);
1442                     if (i + 1 == length) {
1443 #if DEBUG_CHUNK_BUILDING > 1
1444                         fprintf(stderr, " | -> Record last chunk of inline text box!\n");
1445 #endif
1446
1447                         startTextChunk(info);
1448                         SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
1449
1450                         info.assignChunkProperties = false;
1451                         info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
1452                         info.chunk.isTextPath = info.handlingTextPath;
1453                         info.chunk.anchor = text->style()->svgStyle()->textAnchor();
1454                         info.chunk.textLength = textContent->textLength().value(textContent);
1455                         info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
1456
1457                         range.box = curr;
1458                         range.startOffset = i;
1459
1460                         ASSERT(!range.isOpen());
1461                         ASSERT(!range.isClosed());
1462                     }
1463                 }
1464
1465                 // This should only hold true for the first character of the first chunk
1466                 if (isFirstCharacter)
1467                     isFirstCharacter = false;
1468     
1469                 ++info.it;
1470             }
1471
1472 #if DEBUG_CHUNK_BUILDING > 1    
1473             fprintf(stderr, " -> Finished inline text box!\n");
1474 #endif
1475
1476             SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
1477             if (!range.isOpen() && !range.isClosed()) {
1478 #if DEBUG_CHUNK_BUILDING > 1
1479                 fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length);
1480 #endif
1481
1482                 // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk.
1483                 range.endOffset = length;
1484
1485                 if (info.it != svgChars.end()) {
1486 #if DEBUG_CHUNK_BUILDING > 1
1487                     fprintf(stderr, " -> Not at last character yet!\n");
1488 #endif
1489
1490                     // If we're not at the end of the last box to be processed, and if the next
1491                     // character starts a new chunk, then close the current chunk and start a new one.
1492                     if ((*info.it).newTextChunk) {
1493 #if DEBUG_CHUNK_BUILDING > 1
1494                         fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n");
1495 #endif
1496
1497                         closeTextChunk(info);
1498                         startTextChunk(info);
1499                     } else {
1500                         // Just start a new character range
1501                         info.chunk.boxes.append(SVGInlineBoxCharacterRange());
1502
1503 #if DEBUG_CHUNK_BUILDING > 1
1504                         fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n");
1505 #endif
1506                     }
1507                 } else {
1508 #if DEBUG_CHUNK_BUILDING > 1
1509                     fprintf(stderr, " -> Closing final chunk! Finished processing!\n");
1510 #endif
1511
1512                     // Close final chunk, once we're at the end of the last box
1513                     closeTextChunk(info);
1514                 }
1515             }
1516         } else {
1517             ASSERT(curr->isInlineFlowBox());
1518             InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
1519
1520             bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
1521
1522 #if DEBUG_CHUNK_BUILDING > 1
1523             fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath);
1524 #endif
1525
1526             if (isTextPath)
1527                 info.handlingTextPath = true;
1528
1529             buildTextChunks(svgChars, flowBox, info);
1530
1531             if (isTextPath)
1532                 info.handlingTextPath = false;
1533         }
1534     }
1535
1536 #if DEBUG_CHUNK_BUILDING > 1
1537     fprintf(stderr, " <- buildTextChunks(start=%p)\n", start);
1538 #endif
1539 }
1540
1541 const Vector<SVGTextChunk>& SVGRootInlineBox::svgTextChunks() const 
1542 {
1543     return m_svgTextChunks;
1544 }
1545
1546 void SVGRootInlineBox::layoutTextChunks()
1547 {
1548     Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
1549     Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end();
1550
1551     for (; it != end; ++it) {
1552         SVGTextChunk& chunk = *it;
1553
1554 #if DEBUG_CHUNK_BUILDING > 0
1555         {
1556             fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n",
1557                     (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText,
1558                     (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int) (chunk.end - chunk.start));
1559
1560             Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
1561             Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
1562
1563             unsigned int i = 0;
1564             for (; boxIt != boxEnd; ++boxIt) {
1565                 SVGInlineBoxCharacterRange& range = *boxIt; i++;
1566                 fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box);
1567             }
1568         }
1569 #endif
1570
1571         if (chunk.isTextPath)
1572             continue;
1573
1574         // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts.
1575         applyTextLengthCorrectionToTextChunk(chunk);
1576  
1577         // text-anchor is already handled for textPath layouts.
1578         applyTextAnchorToTextChunk(chunk);
1579     }
1580 }
1581
1582 static inline void addPaintServerToTextDecorationInfo(ETextDecoration decoration, SVGTextDecorationInfo& info, RenderObject* object)
1583 {
1584     if (object->style()->svgStyle()->hasFill())
1585         info.fillServerMap.set(decoration, object);
1586
1587     if (object->style()->svgStyle()->hasStroke())
1588         info.strokeServerMap.set(decoration, object);
1589 }
1590
1591 SVGTextDecorationInfo SVGRootInlineBox::retrievePaintServersForTextDecoration(RenderObject* start)
1592 {
1593     ASSERT(start);
1594
1595     Vector<RenderObject*> parentChain;
1596     while ((start = start->parent())) {
1597         parentChain.prepend(start);
1598
1599         // Stop at our direct <text> parent.
1600         if (start->isSVGText())
1601             break;
1602     }
1603
1604     Vector<RenderObject*>::iterator it = parentChain.begin();
1605     Vector<RenderObject*>::iterator end = parentChain.end();
1606
1607     SVGTextDecorationInfo info;
1608
1609     for (; it != end; ++it) {
1610         RenderObject* object = *it;
1611         ASSERT(object);
1612
1613         RenderStyle* style = object->style();
1614         ASSERT(style);
1615
1616         int decorations = style->textDecoration();
1617         if (decorations != NONE) {
1618             if (decorations & OVERLINE)
1619                 addPaintServerToTextDecorationInfo(OVERLINE, info, object);
1620
1621             if (decorations & UNDERLINE)
1622                 addPaintServerToTextDecorationInfo(UNDERLINE, info, object);
1623
1624             if (decorations & LINE_THROUGH)
1625                 addPaintServerToTextDecorationInfo(LINE_THROUGH, info, object);
1626         }
1627     }
1628
1629     return info;
1630 }
1631
1632 void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase* walker, const SVGInlineTextBox* textBox)
1633 {
1634     ASSERT(walker);
1635
1636     Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
1637     Vector<SVGTextChunk>::iterator itEnd = m_svgTextChunks.end();
1638
1639     for (; it != itEnd; ++it) {
1640         SVGTextChunk& curChunk = *it;
1641
1642         Vector<SVGInlineBoxCharacterRange>::iterator boxIt = curChunk.boxes.begin();
1643         Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = curChunk.boxes.end();
1644
1645         InlineBox* lastNotifiedBox = 0;
1646         InlineBox* prevBox = 0;
1647
1648         unsigned int chunkOffset = 0;
1649         bool startedFirstChunk = false;
1650
1651         for (; boxIt != boxEnd; ++boxIt) {
1652             SVGInlineBoxCharacterRange& range = *boxIt;
1653
1654             ASSERT(range.box->isInlineTextBox());
1655             SVGInlineTextBox* rangeTextBox = static_cast<SVGInlineTextBox*>(range.box);
1656
1657             if (textBox && rangeTextBox != textBox) {
1658                 chunkOffset += range.endOffset - range.startOffset;
1659                 continue;
1660             }
1661
1662             // Eventually notify that we started a new chunk
1663             if (!textBox && !startedFirstChunk) {
1664                 startedFirstChunk = true;
1665
1666                 lastNotifiedBox = range.box;
1667                 walker->start(range.box);
1668             } else {
1669                 // Eventually apply new style, as this chunk spans multiple boxes (with possible different styling)
1670                 if (prevBox && prevBox != range.box) {
1671                     lastNotifiedBox = range.box;
1672
1673                     walker->end(prevBox);
1674                     walker->start(lastNotifiedBox);
1675                 }
1676             }
1677
1678             unsigned int length = range.endOffset - range.startOffset;
1679
1680             Vector<SVGChar>::iterator itCharBegin = curChunk.start + chunkOffset;
1681             Vector<SVGChar>::iterator itCharEnd = curChunk.start + chunkOffset + length;
1682             ASSERT(itCharEnd <= curChunk.end);
1683
1684             // Process this chunk portion
1685             if (textBox)
1686                 (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1687             else {
1688                 if (walker->setupFill(range.box))
1689                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1690
1691                 if (walker->setupStroke(range.box))
1692                     (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1693             }
1694
1695             chunkOffset += length;
1696
1697             if (!textBox)
1698                 prevBox = range.box;
1699         }
1700
1701         if (!textBox && startedFirstChunk)
1702             walker->end(lastNotifiedBox);
1703     }
1704 }
1705
1706 } // namespace WebCore
1707
1708 #endif // ENABLE(SVG)