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