fix logic
[personal-kdelibs.git] / khtml / rendering / SVGRootInlineBox.cpp
blob5b24ef105399fb6d009b573744c5dc72e4ffa4b9
1 /*
2 * This file is part of the WebKit project.
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>
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.
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.
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.
25 #include "config.h"
26 #include "wtf/Platform.h"
28 #if ENABLE(SVG)
29 #include "SVGRootInlineBox.h"
31 /*#include "Editor.h"
32 #include "Frame.h"
33 #include "GraphicsContext.h"*/
34 #include "RenderSVGRoot.h"
35 #include "SVGInlineFlowBox.h"
36 #include "SVGInlineTextBox.h"
37 #include "SVGFontElement.h"
38 #include "SVGPaintServer.h"
39 #include "SVGRenderStyleDefs.h"
40 #include "SVGRenderSupport.h"
41 #include "SVGResourceFilter.h"
42 #include "SVGTextPositioningElement.h"
43 #include "SVGURIReference.h"
44 //#include "Text.h"
45 //#include "UnicodeRange.h"
47 #include <float.h>
48 #include <math.h>
50 // Text chunk creation is complex and the whole process
51 // can easily be traced by setting this variable > 0.
52 #define DEBUG_CHUNK_BUILDING 0
54 namespace WebCore {
56 static inline bool isVerticalWritingMode(const SVGRenderStyle* style)
58 return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB;
61 static inline EAlignmentBaseline dominantBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font)
63 ASSERT(text);
65 const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0;
66 ASSERT(style);
68 const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0;
70 EDominantBaseline baseline = style->dominantBaseline();
71 if (baseline == DB_AUTO) {
72 if (isVerticalText)
73 baseline = DB_CENTRAL;
74 else
75 baseline = DB_ALPHABETIC;
78 switch (baseline) {
79 case DB_USE_SCRIPT:
80 // TODO: The dominant-baseline and the baseline-table components are set by
81 // determining the predominant script of the character data content.
82 return AB_ALPHABETIC;
83 case DB_NO_CHANGE:
85 if (parentStyle)
86 return dominantBaselineToShift(isVerticalText, text->parent(), font);
88 ASSERT_NOT_REACHED();
89 return AB_AUTO;
91 case DB_RESET_SIZE:
93 if (parentStyle)
94 return dominantBaselineToShift(isVerticalText, text->parent(), font);
96 ASSERT_NOT_REACHED();
97 return AB_AUTO;
99 case DB_IDEOGRAPHIC:
100 return AB_IDEOGRAPHIC;
101 case DB_ALPHABETIC:
102 return AB_ALPHABETIC;
103 case DB_HANGING:
104 return AB_HANGING;
105 case DB_MATHEMATICAL:
106 return AB_MATHEMATICAL;
107 case DB_CENTRAL:
108 return AB_CENTRAL;
109 case DB_MIDDLE:
110 return AB_MIDDLE;
111 case DB_TEXT_AFTER_EDGE:
112 return AB_TEXT_AFTER_EDGE;
113 case DB_TEXT_BEFORE_EDGE:
114 return AB_TEXT_BEFORE_EDGE;
115 default:
116 ASSERT_NOT_REACHED();
117 return AB_AUTO;
121 static inline float alignmentBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font)
123 ASSERT(text);
125 const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0;
126 ASSERT(style);
128 const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0;
130 EAlignmentBaseline baseline = style->alignmentBaseline();
131 if (baseline == AB_AUTO) {
132 if (parentStyle && style->dominantBaseline() == DB_AUTO)
133 baseline = dominantBaselineToShift(isVerticalText, text->parent(), font);
134 else
135 baseline = dominantBaselineToShift(isVerticalText, text, font);
137 ASSERT(baseline != AB_AUTO);
140 // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
141 switch (baseline) {
142 case AB_BASELINE:
144 if (parentStyle)
145 return dominantBaselineToShift(isVerticalText, text->parent(), font);
147 return 0.0f;
149 case AB_BEFORE_EDGE:
150 case AB_TEXT_BEFORE_EDGE:
151 return font.ascent();
152 case AB_MIDDLE:
153 return font.xHeight() / 2.0f;
154 case AB_CENTRAL:
155 // Not needed, we're taking this into account already for vertical text!
156 // return (font.ascent() - font.descent()) / 2.0f;
157 return 0.0f;
158 case AB_AFTER_EDGE:
159 case AB_TEXT_AFTER_EDGE:
160 case AB_IDEOGRAPHIC:
161 return font.descent();
162 case AB_ALPHABETIC:
163 return 0.0f;
164 case AB_HANGING:
165 return font.ascent() * 8.0f / 10.0f;
166 case AB_MATHEMATICAL:
167 return font.ascent() / 2.0f;
168 default:
169 ASSERT_NOT_REACHED();
170 return 0.0f;
174 static inline float glyphOrientationToAngle(const SVGRenderStyle* svgStyle, bool isVerticalText, const UChar& character)
176 switch (isVerticalText ? svgStyle->glyphOrientationVertical() : svgStyle->glyphOrientationHorizontal()) {
177 case GO_AUTO:
179 // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees.
180 // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees.
181 /*FIXME: khtml porting; unsigned int unicodeRange = findCharUnicodeRange(character);
182 if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic)
183 return 90.0f;*/
185 return 0.0f;
187 case GO_90DEG:
188 return 90.0f;
189 case GO_180DEG:
190 return 180.0f;
191 case GO_270DEG:
192 return 270.0f;
193 case GO_0DEG:
194 default:
195 return 0.0f;
199 static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)
201 return fabsf(fmodf(orientationAngle, 180.0f)) == 0.0f;
204 static inline float calculateGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float orientationAngle, float glyphWidth, float glyphHeight, const Font& font, SVGChar& svgChar, float& xOrientationShift, float& yOrientationShift)
206 bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(orientationAngle);
208 // The function is based on spec requirements:
210 // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of
211 // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph.
213 // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of
214 // 180 degrees,then the current text position is incremented according to the horizontal metrics of the glyph.
216 // vertical orientation handling
217 if (isVerticalText) {
218 if (orientationAngle == 0.0f) {
219 xOrientationShift = -glyphWidth / 2.0f;
220 yOrientationShift = font.ascent();
221 } else if (orientationAngle == 90.0f) {
222 xOrientationShift = -glyphHeight;
223 yOrientationShift = font.descent();
224 svgChar.orientationShiftY = -font.ascent();
225 } else if (orientationAngle == 270.0f) {
226 xOrientationShift = glyphHeight;
227 yOrientationShift = font.descent();
228 svgChar.orientationShiftX = -glyphWidth;
229 svgChar.orientationShiftY = -font.ascent();
230 } else if (orientationAngle == 180.0f) {
231 yOrientationShift = font.ascent();
232 svgChar.orientationShiftX = -glyphWidth / 2.0f;
233 svgChar.orientationShiftY = font.ascent() - font.descent();
236 // vertical advance calculation
237 if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees)
238 return glyphWidth;
240 return glyphHeight;
243 // horizontal orientation handling
244 if (orientationAngle == 90.0f) {
245 xOrientationShift = glyphWidth / 2.0f;
246 yOrientationShift = -font.descent();
247 svgChar.orientationShiftX = -glyphWidth / 2.0f - font.descent();
248 svgChar.orientationShiftY = font.descent();
249 } else if (orientationAngle == 270.0f) {
250 xOrientationShift = -glyphWidth / 2.0f;
251 yOrientationShift = -font.descent();
252 svgChar.orientationShiftX = -glyphWidth / 2.0f + font.descent();
253 svgChar.orientationShiftY = glyphHeight;
254 } else if (orientationAngle == 180.0f) {
255 xOrientationShift = glyphWidth / 2.0f;
256 svgChar.orientationShiftX = -glyphWidth / 2.0f;
257 svgChar.orientationShiftY = font.ascent() - font.descent();
260 // horizontal advance calculation
261 if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees)
262 return glyphHeight;
264 return glyphWidth;
267 static inline void startTextChunk(SVGTextChunkLayoutInfo& info)
269 info.chunk.boxes.clear();
270 info.chunk.boxes.append(SVGInlineBoxCharacterRange());
272 info.chunk.start = info.it;
273 info.assignChunkProperties = true;
276 static inline void closeTextChunk(SVGTextChunkLayoutInfo& info)
278 ASSERT(!info.chunk.boxes.last().isOpen());
279 ASSERT(info.chunk.boxes.last().isClosed());
281 info.chunk.end = info.it;
282 ASSERT(info.chunk.end >= info.chunk.start);
284 info.svgTextChunks.append(info.chunk);
287 RenderSVGRoot* findSVGRootObject(RenderObject* start)
289 // Find associated root inline box
290 while (start && !start->isSVGRoot())
291 start = start->parent();
293 ASSERT(start);
294 ASSERT(start->isSVGRoot());
296 return static_cast<RenderSVGRoot*>(start);
299 static inline FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>& chars)
301 return topLeftPositionOfCharacterRange(chars.begin(), chars.end());
304 FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator it, Vector<SVGChar>::iterator end)
306 float lowX = FLT_MAX, lowY = FLT_MAX;
307 for (; it != end; ++it) {
308 if (it->isHidden())
309 continue;
311 float x = (*it).x;
312 float y = (*it).y;
314 if (x < lowX)
315 lowX = x;
317 if (y < lowY)
318 lowY = y;
321 return FloatPoint(lowX, lowY);
324 // Helper function
325 static float calculateKerning(RenderObject* item)
327 /*FIXME const Font& font = item->style()->font();
328 const SVGRenderStyle* svgStyle = item->style()->svgStyle();
330 float kerning = 0.0f;
331 if (CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->kerning())) {
332 kerning = primitive->getFloatValue();
334 if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE && font.pixelSize() > 0)
335 kerning = kerning / 100.0f * font.pixelSize();
338 return kerning;*/
339 return 0;
342 // Helper class for paint()
343 struct SVGRootInlineBoxPaintWalker {
344 SVGRootInlineBoxPaintWalker(SVGRootInlineBox* rootBox, SVGResourceFilter* rootFilter, RenderObject::PaintInfo paintInfo, int tx, int ty)
345 : m_rootBox(rootBox)
346 , m_chunkStarted(false)
347 , m_paintInfo(paintInfo)
348 , m_savedInfo(paintInfo)
349 , m_boundingBox(tx + rootBox->xPos(), ty + rootBox->yPos(), rootBox->width(), rootBox->height())
350 , m_filter(0)
351 , m_rootFilter(rootFilter)
352 , m_fillPaintServer(0)
353 , m_strokePaintServer(0)
354 , m_fillPaintServerObject(0)
355 , m_strokePaintServerObject(0)
356 , m_tx(tx)
357 , m_ty(ty)
361 ~SVGRootInlineBoxPaintWalker()
363 ASSERT(!m_filter);
364 ASSERT(!m_fillPaintServer);
365 ASSERT(!m_fillPaintServerObject);
366 ASSERT(!m_strokePaintServer);
367 ASSERT(!m_strokePaintServerObject);
368 ASSERT(!m_chunkStarted);
371 void teardownFillPaintServer()
373 if (!m_fillPaintServer)
374 return;
376 m_fillPaintServer->teardown(m_paintInfo.p, 0, m_fillPaintServerObject, ApplyToFillTargetType, true);
378 m_fillPaintServer = 0;
379 m_fillPaintServerObject = 0;
382 void teardownStrokePaintServer()
384 if (!m_strokePaintServer)
385 return;
387 m_strokePaintServer->teardown(m_paintInfo.p, 0, m_strokePaintServerObject, ApplyToStrokeTargetType, true);
389 m_strokePaintServer = 0;
390 m_strokePaintServerObject = 0;
393 void chunkStartCallback(InlineBox* box)
395 ASSERT(!m_chunkStarted);
396 m_chunkStarted = true;
398 InlineFlowBox* flowBox = box->parent();
400 // Initialize text rendering
401 RenderObject* object = flowBox->object();
402 ASSERT(object);
404 m_savedInfo = m_paintInfo;
405 //m_paintInfo.context->save();
407 if (!flowBox->isRootInlineBox())
408 ;//FIXME m_paintInfo.context->concatCTM(m_rootBox->object()->localTransform());
410 //m_paintInfo.context->concatCTM(object->localTransform());
411 m_paintInfo.p->setWorldMatrix(object->localTransform(), true);
413 if (!flowBox->isRootInlineBox()) {
414 prepareToRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_rootFilter);
415 // FIXME khtml m_paintInfo.rect = object->localTransform().inverse().mapRect(m_paintInfo.rect);
419 void chunkEndCallback(InlineBox* box)
421 ASSERT(m_chunkStarted);
422 m_chunkStarted = false;
424 InlineFlowBox* flowBox = box->parent();
426 RenderObject* object = flowBox->object();
427 ASSERT(object);
429 // Clean up last used paint server
430 teardownFillPaintServer();
431 teardownStrokePaintServer();
433 // Finalize text rendering
434 if (!flowBox->isRootInlineBox()) {
435 //FIXME khtml finishRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_savedInfo.context);
436 m_filter = 0;
439 // Restore context & repaint rect
440 //FIXME m_paintInfo.context->restore();
441 //FIXME m_paintInfo.rect = m_savedInfo.rect;
444 bool chunkSetupFillCallback(InlineBox* box)
446 InlineFlowBox* flowBox = box->parent();
448 // Setup fill paint server
449 RenderObject* object = flowBox->object();
450 ASSERT(object);
452 ASSERT(!m_strokePaintServer);
453 teardownFillPaintServer();
455 m_fillPaintServer = SVGPaintServer::fillPaintServer(object->style(), object);
456 if (m_fillPaintServer) {
457 m_fillPaintServer->setup(m_paintInfo.p, 0, object, ApplyToFillTargetType, true);
458 m_fillPaintServerObject = object;
459 return true;
462 return false;
465 bool chunkSetupStrokeCallback(InlineBox* box)
467 InlineFlowBox* flowBox = box->parent();
469 // Setup stroke paint server
470 RenderObject* object = flowBox->object();
471 ASSERT(object);
473 // If we're both stroked & filled, teardown fill paint server before stroking.
474 teardownFillPaintServer();
475 teardownStrokePaintServer();
477 m_strokePaintServer = SVGPaintServer::strokePaintServer(object->style(), object);
479 if (m_strokePaintServer) {
480 m_strokePaintServer->setup(m_paintInfo.p, 0, object, ApplyToStrokeTargetType, true);
481 m_strokePaintServerObject = object;
482 return true;
485 return false;
488 void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const AffineTransform& chunkCtm,
489 const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end)
491 //kDebug() << "text chunk rendering code here" << endl;
492 RenderText* text = textBox->/*textObject()*/renderText();
493 ASSERT(text);
495 RenderStyle* styleToUse = text->style(/*textBox->isFirstLineStyle()*/);
496 ASSERT(styleToUse);
498 startOffset += textBox->start();
500 int textDecorations = styleToUse->textDecorationsInEffect();
502 int textWidth = 0;
503 IntPoint decorationOrigin;
504 SVGTextDecorationInfo info;
506 /*FIXME khtml if (!chunkCtm.isIdentity())
507 m_paintInfo.context->concatCTM(chunkCtm);*/
509 for (Vector<SVGChar>::iterator it = start; it != end; ++it) {
510 if (it->isHidden())
511 continue;
513 // Determine how many characters - starting from the current - can be drawn at once.
514 Vector<SVGChar>::iterator itSearch = it + 1;
515 while (itSearch != end) {
516 if (itSearch->drawnSeperated || itSearch->isHidden())
517 break;
519 itSearch++;
522 /*FIXME khtmlconst*/ UChar* stringStart = text->text() + startOffset + (it - start);
523 unsigned int stringLength = itSearch - it;
525 // Paint decorations, that have to be drawn before the text gets drawn
526 if (textDecorations != TDNONE /*FIXME khtml && m_paintInfo.phase != PaintPhaseSelection*/) {
527 //khtml textWidth = styleToUse->font().width(svgTextRunForInlineTextBox(stringStart, stringLength, styleToUse, textBox, (*it).x));
528 textWidth = styleToUse->htmlFont().width(stringStart, stringLength, 0, stringLength, false /*fast algo*/);
529 decorationOrigin = IntPoint((int) (*it).x, (int) (*it).y - styleToUse->htmlFont().ascent());
530 info = m_rootBox->retrievePaintServersForTextDecoration(text);
533 /*if (textDecorations & UNDERLINE && textWidth != 0.0f)
534 textBox->paintDecoration(UNDERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);
536 if (textDecorations & OVERLINE && textWidth != 0.0f)
537 textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);*/
539 // Paint text
540 SVGPaintServer* activePaintServer = m_fillPaintServer;
541 if (!activePaintServer)
542 activePaintServer = m_strokePaintServer;
544 ASSERT(activePaintServer);
545 textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, activePaintServer);
547 // Paint decorations, that have to be drawn afterwards
548 /* FIXME khtml if (textDecorations & LINE_THROUGH && textWidth != 0.0f)
549 textBox->paintDecoration(LINE_THROUGH, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);*/
551 // Skip processed characters
552 it = itSearch - 1;
555 /* FIXME khtml if (!chunkCtm.isIdentity())
556 m_paintInfo.context->concatCTM(chunkCtm.inverse());*/
559 private:
560 SVGRootInlineBox* m_rootBox;
561 bool m_chunkStarted : 1;
563 RenderObject::PaintInfo m_paintInfo;
564 RenderObject::PaintInfo m_savedInfo;
566 FloatRect m_boundingBox;
567 SVGResourceFilter* m_filter;
568 SVGResourceFilter* m_rootFilter;
570 SVGPaintServer* m_fillPaintServer;
571 SVGPaintServer* m_strokePaintServer;
573 RenderObject* m_fillPaintServerObject;
574 RenderObject* m_strokePaintServerObject;
576 int m_tx;
577 int m_ty;
580 void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty)
582 //if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground)
583 // return;
585 RenderObject::PaintInfo savedInfo(paintInfo);
586 //paintInfo.context->save();
588 SVGResourceFilter* filter = 0;
589 FloatRect boundingBox(tx + xPos(), ty + yPos(), width(), height());
591 // Initialize text rendering
592 paintInfo.p->setWorldMatrix(object()->localTransform(), true);
593 prepareToRenderSVGContent(object(), paintInfo, boundingBox, filter);
594 paintInfo.p->setWorldMatrix(object()->localTransform().inverse(), true);
596 //kDebug() << "paint at" << tx << ty << endl;
597 //kDebug() << "pos: (" << (tx + xPos()) << "," << (ty + yPos()) << ")" << endl;
598 //kDebug() << "size: " << width() << "x" << height() << endl;
600 // Render text, chunk-by-chunk
601 SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty);
602 SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback,
603 &SVGRootInlineBoxPaintWalker::chunkPortionCallback,
604 &SVGRootInlineBoxPaintWalker::chunkStartCallback,
605 &SVGRootInlineBoxPaintWalker::chunkEndCallback,
606 &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback,
607 &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback);
609 walkTextChunks(&walker);
611 // Finalize text rendering
612 //FIXME khtml finishRenderSVGContent(object(), paintInfo, boundingBox, filter, savedInfo.context);
613 //paintInfo.context->restore();
616 int SVGRootInlineBox::placeBoxesHorizontally(int, int& leftPosition, int& rightPosition, bool&)
618 // Remove any offsets caused by RTL text layout
619 leftPosition = 0;
620 rightPosition = 0;
621 return 0;
624 void SVGRootInlineBox::verticallyAlignBoxes(int& heightOfBlock)
626 // height is set by layoutInlineBoxes.
627 heightOfBlock = height();
630 float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range)
632 ASSERT(!range.isOpen());
633 ASSERT(range.isClosed());
634 ASSERT(range.box->isInlineTextBox());
636 InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box);
637 RenderText* text = textBox->renderText();
638 RenderStyle* style = text->style();
640 /*FIXME return style->font().floatWidth(svgTextRunForInlineTextBox(text->characters() + textBox->start() + range.startOffset, range.endOffset - range.startOffset, style, textBox, 0));*/
641 return 0;
644 float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range)
646 ASSERT(!range.isOpen());
647 ASSERT(range.isClosed());
648 ASSERT(range.box->isInlineTextBox());
650 InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box);
651 RenderText* text = textBox->renderText();
652 /*FIXME const Font& font = text->style()->font();
654 return (range.endOffset - range.startOffset) * (font.ascent() + font.descent());*/
655 return 0;
658 /*TextRun svgTextRunForInlineTextBox(const UChar* c, int len, RenderStyle* style, const InlineTextBox* textBox, float xPos)
660 ASSERT(textBox);
661 ASSERT(style);
663 TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->m_reversed, textBox->m_dirOverride || style->visuallyOrdered());
665 #if ENABLE(SVG_FONTS)
666 run.setReferencingRenderObject(textBox->textObject()->parent());
667 #endif
669 // We handle letter & word spacing ourselves
670 run.disableSpacing();
671 return run;
674 static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWidthOnly)
676 float length = 0.0f;
677 Vector<SVGChar>::iterator charIt = chunk.start;
679 Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin();
680 Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end();
682 for (; it != end; ++it) {
683 SVGInlineBoxCharacterRange& range = *it;
685 SVGInlineTextBox* box = static_cast<SVGInlineTextBox*>(range.box);
686 RenderStyle* style = box->object()->style();
688 for (int i = range.startOffset; i < range.endOffset; ++i) {
689 ASSERT(charIt <= chunk.end);
691 // Determine how many characters - starting from the current - can be measured at once.
692 // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width
693 // of a string is not the sum of the boundaries of all contained glyphs.
694 Vector<SVGChar>::iterator itSearch = charIt + 1;
695 Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i;
696 while (itSearch != endSearch) {
697 // No need to check for 'isHidden()' here as this function is not called for text paths.
698 if (itSearch->drawnSeperated)
699 break;
701 itSearch++;
704 unsigned int positionOffset = itSearch - charIt;
706 // Calculate width/height of subrange
707 SVGInlineBoxCharacterRange subRange;
708 subRange.box = range.box;
709 subRange.startOffset = i;
710 subRange.endOffset = i + positionOffset;
712 if (calcWidthOnly)
713 length += cummulatedWidthOfInlineBoxCharacterRange(subRange);
714 else
715 length += cummulatedHeightOfInlineBoxCharacterRange(subRange);
717 // Calculate gap between the previous & current range
718 // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account
719 // so add "40" as width, and analogous for B & C, add "20" as width.
720 if (itSearch > chunk.start && itSearch < chunk.end) {
721 SVGChar& lastCharacter = *(itSearch - 1);
722 SVGChar& currentCharacter = *itSearch;
724 int offset = box->m_reversed ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1;
726 // FIXME: does this need to change to handle multichar glyphs?
727 int charsConsumed = 1;
728 String glyphName;
729 if (calcWidthOnly) {
730 float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName);
731 length += currentCharacter.x - lastCharacter.x - lastGlyphWidth;
732 } else {
733 float lastGlyphHeight = box->calculateGlyphHeight(style, offset, 0);
734 length += currentCharacter.y - lastCharacter.y - lastGlyphHeight;
738 // Advance processed characters
739 i += positionOffset - 1;
740 charIt = itSearch;
744 ASSERT(charIt == chunk.end);
745 return length;
748 static float cummulatedWidthOfTextChunk(SVGTextChunk& chunk)
750 return cummulatedWidthOrHeightOfTextChunk(chunk, true);
753 static float cummulatedHeightOfTextChunk(SVGTextChunk& chunk)
755 return cummulatedWidthOrHeightOfTextChunk(chunk, false);
758 static float calculateTextAnchorShiftForTextChunk(SVGTextChunk& chunk, ETextAnchor anchor)
760 float shift = 0.0f;
762 if (chunk.isVerticalText)
763 shift = cummulatedHeightOfTextChunk(chunk);
764 else
765 shift = cummulatedWidthOfTextChunk(chunk);
767 if (anchor == TA_MIDDLE)
768 shift *= -0.5f;
769 else
770 shift *= -1.0f;
772 return shift;
775 static void applyTextAnchorToTextChunk(SVGTextChunk& chunk)
777 // This method is not called for chunks containing chars aligned on a path.
778 // -> all characters are visible, no need to check for "isHidden()" anywhere.
780 if (chunk.anchor == TA_START)
781 return;
783 float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor);
785 // Apply correction to chunk
786 Vector<SVGChar>::iterator chunkIt = chunk.start;
787 for (; chunkIt != chunk.end; ++chunkIt) {
788 SVGChar& curChar = *chunkIt;
790 if (chunk.isVerticalText)
791 curChar.y += shift;
792 else
793 curChar.x += shift;
796 // Move inline boxes
797 Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
798 Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
800 for (; boxIt != boxEnd; ++boxIt) {
801 SVGInlineBoxCharacterRange& range = *boxIt;
803 InlineBox* curBox = range.box;
804 ASSERT(curBox->isInlineTextBox());
805 ASSERT(curBox->parent() && (curBox->parent()->isRootInlineBox() || curBox->parent()->isInlineFlowBox()));
807 // Move target box
808 if (chunk.isVerticalText)
809 curBox->setYPos(curBox->yPos() + static_cast<int>(shift));
810 else
811 curBox->setXPos(curBox->xPos() + static_cast<int>(shift));
815 static float calculateTextLengthCorrectionForTextChunk(SVGTextChunk& chunk, ELengthAdjust lengthAdjust, float& computedLength)
817 //kDebug() << "text length" << endl;
818 if (chunk.textLength <= 0.0f)
819 return 0.0f;
821 float computedWidth = cummulatedWidthOfTextChunk(chunk);
822 float computedHeight = cummulatedHeightOfTextChunk(chunk);
824 if ((computedWidth <= 0.0f && !chunk.isVerticalText) ||
825 (computedHeight <= 0.0f && chunk.isVerticalText))
826 return 0.0f;
828 if (chunk.isVerticalText)
829 computedLength = computedHeight;
830 else
831 computedLength = computedWidth;
833 if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
834 if (chunk.isVerticalText)
835 chunk.ctm.scale(1.0f, chunk.textLength / computedLength);
836 else
837 chunk.ctm.scale(chunk.textLength / computedLength, 1.0f);
839 return 0.0f;
842 return (chunk.textLength - computedLength) / float(chunk.end - chunk.start);
845 static void applyTextLengthCorrectionToTextChunk(SVGTextChunk& chunk)
847 // This method is not called for chunks containing chars aligned on a path.
848 // -> all characters are visible, no need to check for "isHidden()" anywhere.
850 // lengthAdjust="spacingAndGlyphs" is handled by modifying chunk.ctm
851 float computedLength = 0.0f;
852 float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, computedLength);
854 if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
855 SVGChar& firstChar = *(chunk.start);
857 // Assure we apply the chunk scaling in the right origin
858 AffineTransform newChunkCtm;
859 newChunkCtm.translate(firstChar.x, firstChar.y);
860 newChunkCtm = chunk.ctm * newChunkCtm;
861 newChunkCtm.translate(-firstChar.x, -firstChar.y);
863 chunk.ctm = newChunkCtm;
866 // Apply correction to chunk
867 if (spacingToApply != 0.0f) {
868 Vector<SVGChar>::iterator chunkIt = chunk.start;
869 for (; chunkIt != chunk.end; ++chunkIt) {
870 SVGChar& curChar = *chunkIt;
871 curChar.drawnSeperated = true;
873 if (chunk.isVerticalText)
874 curChar.y += (chunkIt - chunk.start) * spacingToApply;
875 else
876 curChar.x += (chunkIt - chunk.start) * spacingToApply;
881 void SVGRootInlineBox::computePerCharacterLayoutInformation()
883 //kDebug() << "computePerCharacterLayoutInformation()" << endl;
884 // Clean up any previous layout information
885 m_svgChars.clear();
886 m_svgTextChunks.clear();
888 // Build layout information for all contained render objects
889 SVGCharacterLayoutInfo info(m_svgChars);
890 //kDebug() << "before build layout info" << endl;
891 buildLayoutInformation(this, info);
892 //kDebug() << "after build layout info" << endl;
894 // Now all layout information are available for every character
895 // contained in any of our child inline/flow boxes. Build list
896 // of text chunks now, to be able to apply text-anchor shifts.
897 buildTextChunks(m_svgChars, m_svgTextChunks, this);
898 //kDebug() << "after build text chunks" << endl;
900 // Layout all text chunks
901 // text-anchor needs to be applied to individual chunks.
902 layoutTextChunks();
903 //kDebug() << "after layout text chunks" << endl;
905 // Finally the top left position of our box is known.
906 // Propagate this knownledge to our RenderSVGText parent.
907 FloatPoint topLeft = topLeftPositionOfCharacterRange(m_svgChars);
908 object()->setPos((int) floorf(topLeft.x()), (int) floorf(topLeft.y()));
910 // Layout all InlineText/Flow boxes
911 // BEWARE: This requires the root top/left position to be set correctly before!
912 //kDebug() << "before layout inline boxes" << endl;
913 layoutInlineBoxes();
914 //kDebug() << "at the end" << endl;
917 void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo& info)
919 if (start->isRootInlineBox()) {
920 ASSERT(start->object()->element()->hasTagName(SVGNames::textTag));
922 SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(start->object()->element());
923 ASSERT(positioningElement);
924 ASSERT(positioningElement->parentNode());
926 info.addLayoutInformation(positioningElement);
930 LastGlyphInfo lastGlyph;
932 for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
933 if (curr->object()->isText())
934 buildLayoutInformationForTextBox(info, static_cast<InlineTextBox*>(curr), lastGlyph);
935 else {
936 ASSERT(curr->isInlineFlowBox());
937 InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
939 bool isAnchor = flowBox->object()->element()->hasTagName(SVGNames::aTag);
940 bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
942 if (!isTextPath && !isAnchor) {
943 SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(flowBox->object()->element());
944 ASSERT(positioningElement);
945 ASSERT(positioningElement->parentNode());
947 info.addLayoutInformation(positioningElement);
948 } else if (!isAnchor) {
949 info.setInPathLayout(true);
951 // Handle text-anchor/textLength on path, which is special.
952 SVGTextContentElement* textContent = 0;
953 Node* node = flowBox->object()->element();
954 if (node && node->isSVGElement())
955 textContent = static_cast<SVGTextContentElement*>(node);
956 ASSERT(textContent);
958 ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
959 ETextAnchor anchor = flowBox->object()->style()->svgStyle()->textAnchor();
960 float textAnchorStartOffset = 0.0f;
962 // Initialize sub-layout. We need to create text chunks from the textPath
963 // children using our standard layout code, to be able to measure the
964 // text length using our normal methods and not textPath specific hacks.
965 Vector<SVGChar> tempChars;
966 Vector<SVGTextChunk> tempChunks;
968 SVGCharacterLayoutInfo tempInfo(tempChars);
969 buildLayoutInformation(flowBox, tempInfo);
971 buildTextChunks(tempChars, tempChunks, flowBox);
973 Vector<SVGTextChunk>::iterator it = tempChunks.begin();
974 Vector<SVGTextChunk>::iterator end = tempChunks.end();
976 AffineTransform ctm;
977 float computedLength = 0.0f;
979 for (; it != end; ++it) {
980 SVGTextChunk& chunk = *it;
982 // Apply text-length calculation
983 info.pathExtraAdvance += calculateTextLengthCorrectionForTextChunk(chunk, lengthAdjust, computedLength);
985 if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) {
986 info.pathTextLength += computedLength;
987 info.pathChunkLength += chunk.textLength;
990 // Calculate text-anchor start offset
991 if (anchor == TA_START)
992 continue;
994 textAnchorStartOffset += calculateTextAnchorShiftForTextChunk(chunk, anchor);
997 info.addLayoutInformation(flowBox, textAnchorStartOffset);
1000 float shiftxSaved = info.shiftx;
1001 float shiftySaved = info.shifty;
1003 buildLayoutInformation(flowBox, info);
1004 info.processedChunk(shiftxSaved, shiftySaved);
1006 if (isTextPath)
1007 info.setInPathLayout(false);
1012 void SVGRootInlineBox::layoutInlineBoxes()
1014 int lowX = INT_MAX;
1015 int lowY = INT_MAX;
1016 int highX = INT_MIN;
1017 int highY = INT_MIN;
1019 // Layout all child boxes
1020 Vector<SVGChar>::iterator it = m_svgChars.begin();
1021 layoutInlineBoxes(this, it, lowX, highX, lowY, highY);
1022 ASSERT(it == m_svgChars.end());
1025 void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& lowX, int& highX, int& lowY, int& highY)
1027 for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
1028 RenderStyle* style = curr->object()->style();
1029 const Font& font = style->htmlFont();
1031 if (curr->object()->isText()) {
1032 SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(curr);
1033 unsigned length = textBox->len();
1035 SVGChar curChar = *it;
1036 ASSERT(it != m_svgChars.end());
1038 FloatRect stringRect;
1039 for (unsigned i = 0; i < length; ++i) {
1040 ASSERT(it != m_svgChars.end());
1042 if (it->isHidden()) {
1043 ++it;
1044 continue;
1047 stringRect.unite(textBox->calculateGlyphBoundaries(style, textBox->start() + i, *it));
1048 ++it;
1051 IntRect enclosedStringRect = enclosingIntRect(stringRect);
1053 int minX = enclosedStringRect.x();
1054 int maxX = minX + enclosedStringRect.width();
1056 int minY = enclosedStringRect.y();
1057 int maxY = minY + enclosedStringRect.height();
1059 curr->setXPos(minX - object()->xPos());
1060 curr->setWidth(enclosedStringRect.width());
1062 curr->setYPos(minY - object()->yPos());
1063 curr->setBaseline(font.ascent());
1064 curr->setHeight(enclosedStringRect.height());
1066 if (minX < lowX)
1067 lowX = minX;
1069 if (maxX > highX)
1070 highX = maxX;
1072 if (minY < lowY)
1073 lowY = minY;
1075 if (maxY > highY)
1076 highY = maxY;
1077 } else {
1078 ASSERT(curr->isInlineFlowBox());
1080 int minX = INT_MAX;
1081 int minY = INT_MAX;
1082 int maxX = INT_MIN;
1083 int maxY = INT_MIN;
1085 InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
1086 layoutInlineBoxes(flowBox, it, minX, maxX, minY, maxY);
1088 curr->setXPos(minX - object()->xPos());
1089 curr->setWidth(maxX - minX);
1091 curr->setYPos(minY - object()->yPos());
1092 curr->setBaseline(font.ascent());
1093 curr->setHeight(maxY - minY);
1095 if (minX < lowX)
1096 lowX = minX;
1098 if (maxX > highX)
1099 highX = maxX;
1101 if (minY < lowY)
1102 lowY = minY;
1104 if (maxY > highY)
1105 highY = maxY;
1109 if (start->isRootInlineBox()) {
1110 int top = lowY - object()->yPos();
1111 int bottom = highY - object()->yPos();
1113 start->setXPos(lowX - object()->xPos());
1114 start->setYPos(top);
1116 start->setWidth(highX - lowX);
1117 start->setHeight(highY - lowY);
1119 /*FIXME start->setVerticalOverflowPositions(top, bottom);
1120 start->setVerticalSelectionPositions(top, bottom);*/
1124 void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& info, InlineTextBox* textBox, LastGlyphInfo& lastGlyph)
1126 RenderText* text = textBox->renderText();
1127 ASSERT(text);
1129 RenderStyle* style = text->style(/*textBox->isFirstLineStyle()*/);
1130 ASSERT(style);
1132 const Font& font = style->htmlFont();
1133 SVGInlineTextBox* svgTextBox = static_cast<SVGInlineTextBox*>(textBox);
1135 unsigned length = textBox->len();
1137 const SVGRenderStyle* svgStyle = style->svgStyle();
1138 bool isVerticalText = isVerticalWritingMode(svgStyle);
1140 int charsConsumed = 0;
1141 for (unsigned i = 0; i < length; i += charsConsumed) {
1142 SVGChar svgChar;
1144 if (info.inPathLayout())
1145 svgChar.pathData = SVGCharOnPath::create();
1147 float glyphWidth = 0.0f;
1148 float glyphHeight = 0.0f;
1150 int extraCharsAvailable = length - i - 1;
1152 String unicodeStr;
1153 String glyphName;
1154 if (textBox->m_reversed) {
1155 glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName);
1156 glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable);
1157 unicodeStr = String(textBox->renderText()->text()/*->characters()*/ + textBox->end() - i, charsConsumed);
1158 } else {
1159 glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed, glyphName);
1160 glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->start() + i, extraCharsAvailable);
1161 unicodeStr = String(textBox->renderText()->text()/*->characters()*/ + textBox->start() + i, charsConsumed);
1164 bool assignedX = false;
1165 bool assignedY = false;
1167 if (info.xValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && !isVerticalText))) {
1168 if (!isVerticalText)
1169 svgChar.newTextChunk = true;
1171 assignedX = true;
1172 svgChar.drawnSeperated = true;
1173 info.curx = info.xValueNext();
1176 if (info.yValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && isVerticalText))) {
1177 if (isVerticalText)
1178 svgChar.newTextChunk = true;
1180 assignedY = true;
1181 svgChar.drawnSeperated = true;
1182 info.cury = info.yValueNext();
1185 float dx = 0.0f;
1186 float dy = 0.0f;
1188 // Apply x-axis shift
1189 if (info.dxValueAvailable()) {
1190 svgChar.drawnSeperated = true;
1192 dx = info.dxValueNext();
1193 info.dx += dx;
1195 if (!info.inPathLayout())
1196 info.curx += dx;
1199 // Apply y-axis shift
1200 if (info.dyValueAvailable()) {
1201 svgChar.drawnSeperated = true;
1203 dy = info.dyValueNext();
1204 info.dy += dy;
1206 if (!info.inPathLayout())
1207 info.cury += dy;
1210 // Take letter & word spacing and kerning into account
1211 float spacing = 0;//FIXME font.letterSpacing() + calculateKerning(textBox->object()->element()->renderer());
1213 const UChar* currentCharacter = text->text()/*->characters()*/ + (textBox->m_reversed ? textBox->end() - i : textBox->start() + i);
1214 const UChar* lastCharacter = 0;
1216 if (textBox->m_reversed) {
1217 if (i < textBox->end())
1218 lastCharacter = text->text()/*characters()*/ + textBox->end() - i + 1;
1219 } else {
1220 if (i > 0)
1221 lastCharacter = text->text()/*characters()*/ + textBox->start() + i - 1;
1224 if (info.nextDrawnSeperated || spacing != 0.0f) {
1225 info.nextDrawnSeperated = false;
1226 svgChar.drawnSeperated = true;
1229 /*FIXME if (currentCharacter && Font::treatAsSpace(*currentCharacter) && lastCharacter && !Font::treatAsSpace(*lastCharacter)) {
1230 spacing += font.wordSpacing();
1232 if (spacing != 0.0f && !info.inPathLayout())
1233 info.nextDrawnSeperated = true;
1236 float orientationAngle = glyphOrientationToAngle(svgStyle, isVerticalText, *currentCharacter);
1238 float xOrientationShift = 0.0f;
1239 float yOrientationShift = 0.0f;
1240 float glyphAdvance = calculateGlyphAdvanceAndShiftRespectingOrientation(isVerticalText, orientationAngle, glyphWidth, glyphHeight,
1241 font, svgChar, xOrientationShift, yOrientationShift);
1243 // Handle textPath layout mode
1244 if (info.inPathLayout()) {
1245 float extraAdvance = isVerticalText ? dy : dx;
1246 float newOffset = FLT_MIN;
1248 if (assignedX && !isVerticalText)
1249 newOffset = info.curx;
1250 else if (assignedY && isVerticalText)
1251 newOffset = info.cury;
1253 float correctedGlyphAdvance = glyphAdvance;
1255 // Handle lengthAdjust="spacingAndGlyphs" by specifying per-character scale operations
1256 if (info.pathTextLength > 0.0f && info.pathChunkLength > 0.0f) {
1257 if (isVerticalText) {
1258 svgChar.pathData->yScale = info.pathChunkLength / info.pathTextLength;
1259 spacing *= svgChar.pathData->yScale;
1260 correctedGlyphAdvance *= svgChar.pathData->yScale;
1261 } else {
1262 svgChar.pathData->xScale = info.pathChunkLength / info.pathTextLength;
1263 spacing *= svgChar.pathData->xScale;
1264 correctedGlyphAdvance *= svgChar.pathData->xScale;
1268 // Handle letter & word spacing on text path
1269 float pathExtraAdvance = info.pathExtraAdvance;
1270 info.pathExtraAdvance += spacing;
1272 svgChar.pathData->hidden = !info.nextPathLayoutPointAndAngle(correctedGlyphAdvance, extraAdvance, newOffset);
1273 svgChar.drawnSeperated = true;
1275 info.pathExtraAdvance = pathExtraAdvance;
1278 // Apply rotation
1279 if (info.angleValueAvailable())
1280 info.angle = info.angleValueNext();
1282 // Apply baseline-shift
1283 if (info.baselineShiftValueAvailable()) {
1284 svgChar.drawnSeperated = true;
1285 float shift = info.baselineShiftValueNext();
1287 if (isVerticalText)
1288 info.shiftx += shift;
1289 else
1290 info.shifty -= shift;
1293 // Take dominant-baseline / alignment-baseline into account
1294 yOrientationShift += alignmentBaselineToShift(isVerticalText, text, font);
1296 svgChar.x = info.curx;
1297 svgChar.y = info.cury;
1298 svgChar.angle = info.angle;
1300 // For text paths any shift (dx/dy/baseline-shift) has to be applied after the rotation
1301 if (!info.inPathLayout()) {
1302 svgChar.x += info.shiftx + xOrientationShift;
1303 svgChar.y += info.shifty + yOrientationShift;
1305 if (orientationAngle != 0.0f)
1306 svgChar.angle += orientationAngle;
1308 if (svgChar.angle != 0.0f)
1309 svgChar.drawnSeperated = true;
1310 } else {
1311 svgChar.pathData->orientationAngle = orientationAngle;
1313 if (isVerticalText)
1314 svgChar.angle -= 90.0f;
1316 svgChar.pathData->xShift = info.shiftx + xOrientationShift;
1317 svgChar.pathData->yShift = info.shifty + yOrientationShift;
1319 // Translate to glyph midpoint
1320 if (isVerticalText) {
1321 svgChar.pathData->xShift += info.dx;
1322 svgChar.pathData->yShift -= glyphAdvance / 2.0f;
1323 } else {
1324 svgChar.pathData->xShift -= glyphAdvance / 2.0f;
1325 svgChar.pathData->yShift += info.dy;
1329 double kerning = 0.0;
1330 #if ENABLE(SVG_FONTS)
1331 /*FIXME khtml SVGFontElement* svgFont = 0;
1332 if (style->font().isSVGFont())
1333 svgFont = style->font().svgFont();
1335 if (lastGlyph.isValid && style->font().isSVGFont()) {
1336 SVGHorizontalKerningPair kerningPair;
1337 if (svgFont->getHorizontalKerningPairForStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeStr, glyphName, kerningPair))
1338 kerning = kerningPair.kerning;
1341 if (style->font().isSVGFont()) {
1342 lastGlyph.unicode = unicodeStr;
1343 lastGlyph.glyphName = glyphName;
1344 lastGlyph.isValid = true;
1345 } else
1346 lastGlyph.isValid = false;*/
1347 #endif
1349 svgChar.x -= (float)kerning;
1351 // Advance to new position
1352 if (isVerticalText) {
1353 svgChar.drawnSeperated = true;
1354 info.cury += glyphAdvance + spacing;
1355 } else
1356 info.curx += glyphAdvance + spacing - (float)kerning;
1358 // Advance to next character group
1359 for (int k = 0; k < charsConsumed; ++k) {
1360 info.svgChars.append(svgChar);
1361 info.processedSingleCharacter();
1362 svgChar.drawnSeperated = false;
1363 svgChar.newTextChunk = false;
1368 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, Vector<SVGTextChunk>& svgTextChunks, InlineFlowBox* start)
1370 SVGTextChunkLayoutInfo info(svgTextChunks);
1371 info.it = svgChars.begin();
1372 info.chunk.start = svgChars.begin();
1373 info.chunk.end = svgChars.begin();
1375 buildTextChunks(svgChars, start, info);
1376 ASSERT(info.it == svgChars.end());
1379 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* start, SVGTextChunkLayoutInfo& info)
1381 #if DEBUG_CHUNK_BUILDING > 1
1382 fprintf(stderr, " -> buildTextChunks(start=%p)\n", start);
1383 #endif
1385 for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) {
1386 if (curr->object()->isText()) {
1387 InlineTextBox* textBox = static_cast<InlineTextBox*>(curr);
1389 unsigned length = textBox->len();
1390 if (!length)
1391 continue;
1393 #if DEBUG_CHUNK_BUILDING > 1
1394 fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n",
1395 textBox, length, textBox->start(), textBox->end(), (int) info.handlingTextPath);
1396 #endif
1398 RenderText* text = textBox->renderText();
1399 ASSERT(text);
1400 ASSERT(text->element());
1402 SVGTextContentElement* textContent = 0;
1403 Node* node = text->element()->parent();
1404 if (node && node->isSVGElement())
1405 textContent = static_cast<SVGTextContentElement*>(node);
1406 ASSERT(textContent);
1408 // Start new character range for the first chunk
1409 bool isFirstCharacter = info.svgTextChunks.isEmpty() && info.chunk.start == info.it && info.chunk.start == info.chunk.end;
1410 if (isFirstCharacter) {
1411 ASSERT(info.chunk.boxes.isEmpty());
1412 info.chunk.boxes.append(SVGInlineBoxCharacterRange());
1413 } else
1414 ASSERT(!info.chunk.boxes.isEmpty());
1416 // Walk string to find out new chunk positions, if existent
1417 for (unsigned i = 0; i < length; ++i) {
1418 ASSERT(info.it != svgChars.end());
1420 SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
1421 if (range.isOpen()) {
1422 range.box = curr;
1423 range.startOffset = (i == 0 ? 0 : i - 1);
1425 #if DEBUG_CHUNK_BUILDING > 1
1426 fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset);
1427 #endif
1430 // If a new (or the first) chunk has been started, record it's text-anchor and writing mode.
1431 if (info.assignChunkProperties) {
1432 info.assignChunkProperties = false;
1434 info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
1435 info.chunk.isTextPath = info.handlingTextPath;
1436 info.chunk.anchor = text->style()->svgStyle()->textAnchor();
1437 info.chunk.textLength = textContent->textLength().value();
1438 info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
1440 #if DEBUG_CHUNK_BUILDING > 1
1441 fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", info.chunk.isVerticalText, info.chunk.anchor);
1442 #endif
1445 if (i > 0 && !isFirstCharacter && (*info.it).newTextChunk) {
1446 // Close mid chunk & character range
1447 ASSERT(!range.isOpen());
1448 ASSERT(!range.isClosed());
1450 range.endOffset = i;
1451 closeTextChunk(info);
1453 #if DEBUG_CHUNK_BUILDING > 1
1454 fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset);
1455 #endif
1457 // Prepare for next chunk, if we're not at the end
1458 startTextChunk(info);
1459 if (i + 1 == length) {
1460 #if DEBUG_CHUNK_BUILDING > 1
1461 fprintf(stderr, " | -> Record last chunk of inline text box!\n");
1462 #endif
1464 startTextChunk(info);
1465 SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
1467 info.assignChunkProperties = false;
1468 info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle());
1469 info.chunk.isTextPath = info.handlingTextPath;
1470 info.chunk.anchor = text->style()->svgStyle()->textAnchor();
1471 info.chunk.textLength = textContent->textLength().value();
1472 info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust();
1474 range.box = curr;
1475 range.startOffset = i;
1477 ASSERT(!range.isOpen());
1478 ASSERT(!range.isClosed());
1482 // This should only hold true for the first character of the first chunk
1483 if (isFirstCharacter)
1484 isFirstCharacter = false;
1486 ++info.it;
1489 #if DEBUG_CHUNK_BUILDING > 1
1490 fprintf(stderr, " -> Finished inline text box!\n");
1491 #endif
1493 SVGInlineBoxCharacterRange& range = info.chunk.boxes.last();
1494 if (!range.isOpen() && !range.isClosed()) {
1495 #if DEBUG_CHUNK_BUILDING > 1
1496 fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length);
1497 #endif
1499 // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk.
1500 range.endOffset = length;
1502 if (info.it != svgChars.end()) {
1503 #if DEBUG_CHUNK_BUILDING > 1
1504 fprintf(stderr, " -> Not at last character yet!\n");
1505 #endif
1507 // If we're not at the end of the last box to be processed, and if the next
1508 // character starts a new chunk, then close the current chunk and start a new one.
1509 if ((*info.it).newTextChunk) {
1510 #if DEBUG_CHUNK_BUILDING > 1
1511 fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n");
1512 #endif
1514 closeTextChunk(info);
1515 startTextChunk(info);
1516 } else {
1517 // Just start a new character range
1518 info.chunk.boxes.append(SVGInlineBoxCharacterRange());
1520 #if DEBUG_CHUNK_BUILDING > 1
1521 fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n");
1522 #endif
1524 } else {
1525 #if DEBUG_CHUNK_BUILDING > 1
1526 fprintf(stderr, " -> Closing final chunk! Finished processing!\n");
1527 #endif
1529 // Close final chunk, once we're at the end of the last box
1530 closeTextChunk(info);
1533 } else {
1534 ASSERT(curr->isInlineFlowBox());
1535 InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr);
1537 bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag);
1539 #if DEBUG_CHUNK_BUILDING > 1
1540 fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath);
1541 #endif
1543 if (isTextPath)
1544 info.handlingTextPath = true;
1546 buildTextChunks(svgChars, flowBox, info);
1548 if (isTextPath)
1549 info.handlingTextPath = false;
1553 #if DEBUG_CHUNK_BUILDING > 1
1554 fprintf(stderr, " <- buildTextChunks(start=%p)\n", start);
1555 #endif
1558 const Vector<SVGTextChunk>& SVGRootInlineBox::svgTextChunks() const
1560 return m_svgTextChunks;
1563 void SVGRootInlineBox::layoutTextChunks()
1565 Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
1566 Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end();
1568 for (; it != end; ++it) {
1569 SVGTextChunk& chunk = *it;
1571 #if DEBUG_CHUNK_BUILDING > 0
1573 fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n",
1574 (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText,
1575 (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int) (chunk.end - chunk.start));
1577 Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin();
1578 Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end();
1580 unsigned int i = 0;
1581 for (; boxIt != boxEnd; ++boxIt) {
1582 SVGInlineBoxCharacterRange& range = *boxIt; i++;
1583 fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box);
1586 #endif
1588 if (chunk.isTextPath)
1589 continue;
1591 // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts.
1592 applyTextLengthCorrectionToTextChunk(chunk);
1594 // text-anchor is already handled for textPath layouts.
1595 applyTextAnchorToTextChunk(chunk);
1599 static inline void addPaintServerToTextDecorationInfo(ETextDecoration decoration, SVGTextDecorationInfo& info, RenderObject* object)
1601 if (object->style()->svgStyle()->hasFill())
1602 info.fillServerMap.set(decoration, object);
1604 if (object->style()->svgStyle()->hasStroke())
1605 info.strokeServerMap.set(decoration, object);
1608 SVGTextDecorationInfo SVGRootInlineBox::retrievePaintServersForTextDecoration(RenderObject* start)
1610 ASSERT(start);
1612 Vector<RenderObject*> parentChain;
1613 while ((start = start->parent())) {
1614 parentChain.prepend(start);
1616 // Stop at our direct <text> parent.
1617 if (start->isSVGText())
1618 break;
1621 Vector<RenderObject*>::iterator it = parentChain.begin();
1622 Vector<RenderObject*>::iterator end = parentChain.end();
1624 SVGTextDecorationInfo info;
1626 for (; it != end; ++it) {
1627 RenderObject* object = *it;
1628 ASSERT(object);
1630 RenderStyle* style = object->style();
1631 ASSERT(style);
1633 int decorations = style->textDecoration();
1634 if (decorations != NONE) {
1635 if (decorations & OVERLINE)
1636 addPaintServerToTextDecorationInfo(OVERLINE, info, object);
1638 if (decorations & UNDERLINE)
1639 addPaintServerToTextDecorationInfo(UNDERLINE, info, object);
1641 if (decorations & LINE_THROUGH)
1642 addPaintServerToTextDecorationInfo(LINE_THROUGH, info, object);
1646 return info;
1649 void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase* walker, const SVGInlineTextBox* textBox)
1651 ASSERT(walker);
1653 Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin();
1654 Vector<SVGTextChunk>::iterator itEnd = m_svgTextChunks.end();
1656 for (; it != itEnd; ++it) {
1657 SVGTextChunk& curChunk = *it;
1659 Vector<SVGInlineBoxCharacterRange>::iterator boxIt = curChunk.boxes.begin();
1660 Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = curChunk.boxes.end();
1662 InlineBox* lastNotifiedBox = 0;
1663 InlineBox* prevBox = 0;
1665 unsigned int chunkOffset = 0;
1666 bool startedFirstChunk = false;
1668 for (; boxIt != boxEnd; ++boxIt) {
1669 SVGInlineBoxCharacterRange& range = *boxIt;
1671 ASSERT(range.box->isInlineTextBox());
1672 SVGInlineTextBox* rangeTextBox = static_cast<SVGInlineTextBox*>(range.box);
1674 if (textBox && rangeTextBox != textBox) {
1675 chunkOffset += range.endOffset - range.startOffset;
1676 continue;
1679 // Eventually notify that we started a new chunk
1680 if (!textBox && !startedFirstChunk) {
1681 startedFirstChunk = true;
1683 lastNotifiedBox = range.box;
1684 walker->start(range.box);
1685 } else {
1686 // Eventually apply new style, as this chunk spans multiple boxes (with possible different styling)
1687 if (prevBox && prevBox != range.box) {
1688 lastNotifiedBox = range.box;
1690 walker->end(prevBox);
1691 walker->start(lastNotifiedBox);
1695 unsigned int length = range.endOffset - range.startOffset;
1697 Vector<SVGChar>::iterator itCharBegin = curChunk.start + chunkOffset;
1698 Vector<SVGChar>::iterator itCharEnd = curChunk.start + chunkOffset + length;
1699 ASSERT(itCharEnd <= curChunk.end);
1701 // Process this chunk portion
1702 if (textBox)
1703 (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1704 else {
1705 if (walker->setupFill(range.box))
1706 (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1708 if (walker->setupStroke(range.box))
1709 (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd);
1712 chunkOffset += length;
1714 if (!textBox)
1715 prevBox = range.box;
1718 if (!textBox && startedFirstChunk)
1719 walker->end(lastNotifiedBox);
1723 } // namespace WebCore
1725 #endif // ENABLE(SVG)