fc95f3713bd1c0938c167c8411c11939428b9eca
[WebKit-https.git] / Source / WebCore / rendering / svg / SVGTextLayoutEngine.cpp
1 /*
2  * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "SVGTextLayoutEngine.h"
22
23 #include "RenderSVGTextPath.h"
24 #include "SVGElement.h"
25 #include "SVGInlineTextBox.h"
26 #include "SVGLengthContext.h"
27 #include "SVGTextLayoutEngineBaseline.h"
28 #include "SVGTextLayoutEngineSpacing.h"
29
30 // Set to a value > 0 to dump the text fragments
31 #define DUMP_TEXT_FRAGMENTS 0
32
33 namespace WebCore {
34
35 SVGTextLayoutEngine::SVGTextLayoutEngine(Vector<SVGTextLayoutAttributes*>& layoutAttributes)
36     : m_layoutAttributes(layoutAttributes)
37     , m_layoutAttributesPosition(0)
38     , m_logicalCharacterOffset(0)
39     , m_logicalMetricsListOffset(0)
40     , m_visualCharacterOffset(0)
41     , m_visualMetricsListOffset(0)
42     , m_x(0)
43     , m_y(0)
44     , m_dx(0)
45     , m_dy(0)
46     , m_isVerticalText(false)
47     , m_inPathLayout(false)
48     , m_textPathLength(0)
49     , m_textPathCurrentOffset(0)
50     , m_textPathSpacing(0)
51     , m_textPathScaling(1)
52 {
53     ASSERT(!m_layoutAttributes.isEmpty());
54 }
55
56 void SVGTextLayoutEngine::updateCharacerPositionIfNeeded(float& x, float& y)
57 {
58     if (m_inPathLayout)
59         return;
60
61     // Replace characters x/y position, with the current text position plus any
62     // relative adjustments, if it doesn't specify an absolute position itself.
63     if (x == SVGTextLayoutAttributes::emptyValue()) 
64         x = m_x + m_dx;
65
66     if (y == SVGTextLayoutAttributes::emptyValue())
67         y = m_y + m_dy;
68
69     m_dx = 0;
70     m_dy = 0;
71 }
72
73 void SVGTextLayoutEngine::updateCurrentTextPosition(float x, float y, float glyphAdvance)
74 {
75     // Update current text position after processing the character.
76     if (m_isVerticalText) {
77         m_x = x;
78         m_y = y + glyphAdvance;
79     } else {
80         m_x = x + glyphAdvance;
81         m_y = y;
82     }
83 }
84
85 void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(float dx, float dy)
86 {
87     // Update relative positioning information.
88     if (dx == SVGTextLayoutAttributes::emptyValue() && dy == SVGTextLayoutAttributes::emptyValue())
89         return;
90
91     if (dx == SVGTextLayoutAttributes::emptyValue())
92         dx = 0;
93     if (dy == SVGTextLayoutAttributes::emptyValue())
94         dy = 0;
95
96     if (m_inPathLayout) {
97         if (m_isVerticalText) {
98             m_dx += dx;
99             m_dy = dy;
100         } else {
101             m_dx = dx;
102             m_dy += dy;
103         }
104
105         return;
106     }
107
108     m_dx = dx;
109     m_dy = dy;
110 }
111
112 void SVGTextLayoutEngine::recordTextFragment(SVGInlineTextBox* textBox, Vector<SVGTextMetrics>& textMetricsValues)
113 {
114     ASSERT(!m_currentTextFragment.length);
115     ASSERT(m_visualMetricsListOffset > 0);
116
117     // Figure out length of fragment.
118     m_currentTextFragment.length = m_visualCharacterOffset - m_currentTextFragment.characterOffset;
119
120     // Figure out fragment metrics.
121     SVGTextMetrics& lastCharacterMetrics = textMetricsValues.at(m_visualMetricsListOffset - 1);
122     m_currentTextFragment.width = lastCharacterMetrics.width();
123     m_currentTextFragment.height = lastCharacterMetrics.height();
124
125     if (m_currentTextFragment.length > 1) {
126         // SVGTextLayoutAttributesBuilder assures that the length of the range is equal to the sum of the individual lengths of the glyphs.
127         float length = 0;
128         if (m_isVerticalText) {
129             for (unsigned i = m_currentTextFragment.metricsListOffset; i < m_visualMetricsListOffset; ++i)
130                 length += textMetricsValues.at(i).height();
131             m_currentTextFragment.height = length;
132         } else {
133             for (unsigned i = m_currentTextFragment.metricsListOffset; i < m_visualMetricsListOffset; ++i)
134                 length += textMetricsValues.at(i).width();
135             m_currentTextFragment.width = length;
136         }
137     }
138
139     textBox->textFragments().append(m_currentTextFragment);
140     m_currentTextFragment = SVGTextFragment();
141 }
142
143 bool SVGTextLayoutEngine::parentDefinesTextLength(RenderObject* parent) const
144 {
145     RenderObject* currentParent = parent;
146     while (currentParent) {
147         if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(currentParent)) {
148             SVGLengthContext lengthContext(textContentElement);
149             if (textContentElement->lengthAdjust() == SVGLengthAdjustSpacing && textContentElement->specifiedTextLength().value(lengthContext) > 0)
150                 return true;
151         }
152
153         if (currentParent->isSVGText())
154             return false;
155
156         currentParent = currentParent->parent();
157     }
158
159     ASSERT_NOT_REACHED();
160     return false;
161 }
162
163 void SVGTextLayoutEngine::beginTextPathLayout(RenderObject* object, SVGTextLayoutEngine& lineLayout)
164 {
165     ASSERT(object);
166
167     m_inPathLayout = true;
168     RenderSVGTextPath& textPath = downcast<RenderSVGTextPath>(*object);
169
170     m_textPath = textPath.layoutPath();
171     if (m_textPath.isEmpty())
172         return;
173     m_textPathStartOffset = textPath.startOffset();
174     m_textPathLength = m_textPath.length();
175     if (m_textPathStartOffset > 0 && m_textPathStartOffset <= 1)
176         m_textPathStartOffset *= m_textPathLength;
177
178     float totalLength = 0;
179     unsigned totalCharacters = 0;
180
181     lineLayout.m_chunkLayoutBuilder.buildTextChunks(lineLayout.m_lineLayoutBoxes);
182     const Vector<SVGTextChunk>& textChunks = lineLayout.m_chunkLayoutBuilder.textChunks();
183
184     unsigned size = textChunks.size();
185     for (unsigned i = 0; i < size; ++i) {
186         const SVGTextChunk& chunk = textChunks.at(i);
187
188         float length = 0;
189         unsigned characters = 0;
190         chunk.calculateLength(length, characters);
191
192         // Handle text-anchor as additional start offset for text paths.
193         m_textPathStartOffset += chunk.calculateTextAnchorShift(length);
194
195         totalLength += length;
196         totalCharacters += characters;
197     }
198
199     m_textPathCurrentOffset = m_textPathStartOffset;
200
201     // Eventually handle textLength adjustments.
202     SVGLengthAdjustType lengthAdjust = SVGLengthAdjustUnknown;
203     float desiredTextLength = 0;
204
205     if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(&textPath)) {
206         SVGLengthContext lengthContext(textContentElement);
207         lengthAdjust = textContentElement->lengthAdjust();
208         desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext);
209     }
210
211     if (!desiredTextLength)
212         return;
213
214     if (lengthAdjust == SVGLengthAdjustSpacing)
215         m_textPathSpacing = (desiredTextLength - totalLength) / totalCharacters;
216     else
217         m_textPathScaling = desiredTextLength / totalLength;
218 }
219
220 void SVGTextLayoutEngine::endTextPathLayout()
221 {
222     m_inPathLayout = false;
223     m_textPath = Path();
224     m_textPathLength = 0;
225     m_textPathStartOffset = 0;
226     m_textPathCurrentOffset = 0;
227     m_textPathSpacing = 0;
228     m_textPathScaling = 1;
229 }
230
231 void SVGTextLayoutEngine::layoutInlineTextBox(SVGInlineTextBox* textBox)
232 {
233     ASSERT(textBox);
234
235     RenderSVGInlineText& text = textBox->renderer();
236     ASSERT(text.parent());
237     ASSERT(text.parent()->element());
238     ASSERT(text.parent()->element()->isSVGElement());
239
240     const RenderStyle& style = text.style();
241
242     textBox->clearTextFragments();
243     m_isVerticalText = style.svgStyle().isVerticalWritingMode();
244     layoutTextOnLineOrPath(textBox, &text, &style);
245
246     if (m_inPathLayout) {
247         m_pathLayoutBoxes.append(textBox);
248         return;
249     }
250
251     m_lineLayoutBoxes.append(textBox);
252 }
253
254 #if DUMP_TEXT_FRAGMENTS > 0
255 static inline void dumpTextBoxes(Vector<SVGInlineTextBox*>& boxes)
256 {
257     unsigned boxCount = boxes.size();
258     fprintf(stderr, "Dumping all text fragments in text sub tree, %i boxes\n", boxCount);
259
260     for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
261         SVGInlineTextBox* textBox = boxes.at(boxPosition);
262         Vector<SVGTextFragment>& fragments = textBox->textFragments();
263         fprintf(stderr, "-> Box %i: Dumping text fragments for SVGInlineTextBox, textBox=%p, textRenderer=%p\n", boxPosition, textBox, textBox->renderer());
264         fprintf(stderr, "        textBox properties, start=%i, len=%i, box direction=%i\n", textBox->start(), textBox->len(), textBox->direction());
265         fprintf(stderr, "   textRenderer properties, textLength=%i\n", textBox->renderer()->textLength());
266
267         const UChar* characters = textBox->renderer()->characters();
268
269         unsigned fragmentCount = fragments.size();
270         for (unsigned i = 0; i < fragmentCount; ++i) {
271             SVGTextFragment& fragment = fragments.at(i);
272             String fragmentString(characters + fragment.characterOffset, fragment.length);
273             fprintf(stderr, "    -> Fragment %i, x=%lf, y=%lf, width=%lf, height=%lf, characterOffset=%i, length=%i, characters='%s'\n"
274                           , i, fragment.x, fragment.y, fragment.width, fragment.height, fragment.characterOffset, fragment.length, fragmentString.utf8().data());
275         }
276     }
277 }
278 #endif
279
280 void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& boxes)
281 {
282     unsigned boxCount = boxes.size();
283     if (!boxCount)
284         return;
285
286     AffineTransform textBoxTransformation;
287     for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
288         SVGInlineTextBox* textBox = boxes.at(boxPosition);
289         Vector<SVGTextFragment>& fragments = textBox->textFragments();
290
291         unsigned fragmentCount = fragments.size();
292         for (unsigned i = 0; i < fragmentCount; ++i) {
293             m_chunkLayoutBuilder.transformationForTextBox(textBox, textBoxTransformation);
294             if (textBoxTransformation.isIdentity())
295                 continue;
296             ASSERT(fragments[i].lengthAdjustTransform.isIdentity());
297             fragments[i].lengthAdjustTransform = textBoxTransformation;
298         }
299     }
300
301     boxes.clear();
302 }
303
304 void SVGTextLayoutEngine::finishLayout()
305 {
306     // After all text fragments are stored in their correpsonding SVGInlineTextBoxes, we can layout individual text chunks.
307     // Chunk layouting is only performed for line layout boxes, not for path layout, where it has already been done.
308     m_chunkLayoutBuilder.layoutTextChunks(m_lineLayoutBoxes);
309
310     // Finalize transform matrices, after the chunk layout corrections have been applied, and all fragment x/y positions are finalized.
311     if (!m_lineLayoutBoxes.isEmpty()) {
312 #if DUMP_TEXT_FRAGMENTS > 0
313         fprintf(stderr, "Line layout: ");
314         dumpTextBoxes(m_lineLayoutBoxes);
315 #endif
316
317         finalizeTransformMatrices(m_lineLayoutBoxes);
318     }
319
320     if (!m_pathLayoutBoxes.isEmpty()) {
321 #if DUMP_TEXT_FRAGMENTS > 0
322         fprintf(stderr, "Path layout: ");
323         dumpTextBoxes(m_pathLayoutBoxes);
324 #endif
325
326         finalizeTransformMatrices(m_pathLayoutBoxes);
327     }
328 }
329
330 bool SVGTextLayoutEngine::currentLogicalCharacterAttributes(SVGTextLayoutAttributes*& logicalAttributes)
331 {
332     if (m_layoutAttributesPosition == m_layoutAttributes.size())
333         return false;
334
335     logicalAttributes = m_layoutAttributes[m_layoutAttributesPosition];
336     ASSERT(logicalAttributes);
337
338     if (m_logicalCharacterOffset != logicalAttributes->context().textLength())
339         return true;
340
341     ++m_layoutAttributesPosition;
342     if (m_layoutAttributesPosition == m_layoutAttributes.size())
343         return false;
344
345     logicalAttributes = m_layoutAttributes[m_layoutAttributesPosition];
346     m_logicalMetricsListOffset = 0;
347     m_logicalCharacterOffset = 0;
348     return true;
349 }
350
351 bool SVGTextLayoutEngine::currentLogicalCharacterMetrics(SVGTextLayoutAttributes*& logicalAttributes, SVGTextMetrics& logicalMetrics)
352 {
353     Vector<SVGTextMetrics>* textMetricsValues = &logicalAttributes->textMetricsValues();
354     unsigned textMetricsSize = textMetricsValues->size();
355     while (true) {
356         if (m_logicalMetricsListOffset == textMetricsSize) {
357             if (!currentLogicalCharacterAttributes(logicalAttributes))
358                 return false;
359
360             textMetricsValues = &logicalAttributes->textMetricsValues();
361             textMetricsSize = textMetricsValues->size();
362             continue;
363         }
364
365         ASSERT(textMetricsSize);
366         ASSERT_WITH_SECURITY_IMPLICATION(m_logicalMetricsListOffset < textMetricsSize);
367         logicalMetrics = textMetricsValues->at(m_logicalMetricsListOffset);
368         if (logicalMetrics.isEmpty() || (!logicalMetrics.width() && !logicalMetrics.height())) {
369             advanceToNextLogicalCharacter(logicalMetrics);
370             continue;
371         }
372
373         // Stop if we found the next valid logical text metrics object.
374         return true;
375     }
376
377     ASSERT_NOT_REACHED();
378     return true;
379 }
380
381 bool SVGTextLayoutEngine::currentVisualCharacterMetrics(SVGInlineTextBox* textBox, Vector<SVGTextMetrics>& visualMetricsValues, SVGTextMetrics& visualMetrics)
382 {
383     ASSERT(!visualMetricsValues.isEmpty());
384     unsigned textMetricsSize = visualMetricsValues.size();
385     unsigned boxStart = textBox->start();
386     unsigned boxLength = textBox->len();
387
388     if (m_visualMetricsListOffset == textMetricsSize)
389         return false;
390
391     while (m_visualMetricsListOffset < textMetricsSize) {
392         // Advance to text box start location.
393         if (m_visualCharacterOffset < boxStart) {
394             advanceToNextVisualCharacter(visualMetricsValues[m_visualMetricsListOffset]);
395             continue;
396         }
397
398         // Stop if we've finished processing this text box.
399         if (m_visualCharacterOffset >= boxStart + boxLength)
400             return false;
401
402         visualMetrics = visualMetricsValues[m_visualMetricsListOffset];
403         return true;
404     }
405
406     return false;
407 }
408
409 void SVGTextLayoutEngine::advanceToNextLogicalCharacter(const SVGTextMetrics& logicalMetrics)
410 {
411     ++m_logicalMetricsListOffset;
412     m_logicalCharacterOffset += logicalMetrics.length();
413 }
414
415 void SVGTextLayoutEngine::advanceToNextVisualCharacter(const SVGTextMetrics& visualMetrics)
416 {
417     ++m_visualMetricsListOffset;
418     m_visualCharacterOffset += visualMetrics.length();
419 }
420
421 void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, RenderSVGInlineText* text, const RenderStyle* style)
422 {
423     if (m_inPathLayout && m_textPath.isEmpty())
424         return;
425
426     RenderElement* textParent = text->parent();
427     ASSERT(textParent);
428     SVGElement* lengthContext = downcast<SVGElement>(textParent->element());
429     
430     bool definesTextLength = parentDefinesTextLength(textParent);
431
432     const SVGRenderStyle& svgStyle = style->svgStyle();
433
434     m_visualMetricsListOffset = 0;
435     m_visualCharacterOffset = 0;
436
437     Vector<SVGTextMetrics>& visualMetricsValues = text->layoutAttributes()->textMetricsValues();
438     ASSERT(!visualMetricsValues.isEmpty());
439
440     auto upconvertedCharacters = StringView(text->text()).upconvertedCharacters();
441     const UChar* characters = upconvertedCharacters;
442     const FontCascade& font = style->fontCascade();
443
444     SVGTextLayoutEngineSpacing spacingLayout(font);
445     SVGTextLayoutEngineBaseline baselineLayout(font);
446
447     bool didStartTextFragment = false;
448     bool applySpacingToNextCharacter = false;
449
450     float lastAngle = 0;
451     float baselineShift = baselineLayout.calculateBaselineShift(&svgStyle, lengthContext);
452     baselineShift -= baselineLayout.calculateAlignmentBaselineShift(m_isVerticalText, text);
453
454     // Main layout algorithm.
455     while (true) {
456         // Find the start of the current text box in this list, respecting ligatures.
457         SVGTextMetrics visualMetrics(SVGTextMetrics::SkippedSpaceMetrics);
458         if (!currentVisualCharacterMetrics(textBox, visualMetricsValues, visualMetrics))
459             break;
460
461         if (visualMetrics.isEmpty()) {
462             advanceToNextVisualCharacter(visualMetrics);
463             continue;
464         }
465
466         SVGTextLayoutAttributes* logicalAttributes = 0;
467         if (!currentLogicalCharacterAttributes(logicalAttributes))
468             break;
469
470         ASSERT(logicalAttributes);
471         SVGTextMetrics logicalMetrics(SVGTextMetrics::SkippedSpaceMetrics);
472         if (!currentLogicalCharacterMetrics(logicalAttributes, logicalMetrics))
473             break;
474
475         SVGCharacterDataMap& characterDataMap = logicalAttributes->characterDataMap();
476         SVGCharacterData data;
477         SVGCharacterDataMap::iterator it = characterDataMap.find(m_logicalCharacterOffset + 1);
478         if (it != characterDataMap.end())
479             data = it->value;
480
481         float x = data.x;
482         float y = data.y;
483
484         // When we've advanced to the box start offset, determine using the original x/y values,
485         // whether this character starts a new text chunk, before doing any further processing.
486         if (m_visualCharacterOffset == textBox->start())
487             textBox->setStartsNewTextChunk(logicalAttributes->context().characterStartsNewTextChunk(m_logicalCharacterOffset));
488
489         float angle = data.rotate == SVGTextLayoutAttributes::emptyValue() ? 0 : data.rotate;
490
491         // Calculate glyph orientation angle.
492         const UChar* currentCharacter = characters + m_visualCharacterOffset;
493         float orientationAngle = baselineLayout.calculateGlyphOrientationAngle(m_isVerticalText, &svgStyle, *currentCharacter);
494
495         // Calculate glyph advance & x/y orientation shifts.
496         float xOrientationShift = 0;
497         float yOrientationShift = 0;
498         float glyphAdvance = baselineLayout.calculateGlyphAdvanceAndOrientation(m_isVerticalText, visualMetrics, orientationAngle, xOrientationShift, yOrientationShift);
499
500         // Assign current text position to x/y values, if needed.
501         updateCharacerPositionIfNeeded(x, y);
502
503         // Apply dx/dy value adjustments to current text position, if needed.
504         updateRelativePositionAdjustmentsIfNeeded(data.dx, data.dy);
505
506         // Calculate SVG Fonts kerning, if needed.
507         float kerning = spacingLayout.calculateSVGKerning(m_isVerticalText, visualMetrics.glyph());
508
509         // Calculate CSS 'kerning', 'letter-spacing' and 'word-spacing' for next character, if needed.
510         float spacing = spacingLayout.calculateCSSKerningAndSpacing(&svgStyle, lengthContext, currentCharacter);
511
512         float textPathOffset = 0;
513         if (m_inPathLayout) {
514             float scaledGlyphAdvance = glyphAdvance * m_textPathScaling;
515             if (m_isVerticalText) {
516                 // If there's an absolute y position available, it marks the beginning of a new position along the path.
517                 if (y != SVGTextLayoutAttributes::emptyValue())
518                     m_textPathCurrentOffset = y + m_textPathStartOffset;
519
520                 m_textPathCurrentOffset += m_dy - kerning;
521                 m_dy = 0;
522
523                 // Apply dx/dy correction and setup translations that move to the glyph midpoint.
524                 xOrientationShift += m_dx + baselineShift;
525                 yOrientationShift -= scaledGlyphAdvance / 2;
526             } else {
527                 // If there's an absolute x position available, it marks the beginning of a new position along the path.
528                 if (x != SVGTextLayoutAttributes::emptyValue())
529                     m_textPathCurrentOffset = x + m_textPathStartOffset;
530
531                 m_textPathCurrentOffset += m_dx - kerning;
532                 m_dx = 0;
533
534                 // Apply dx/dy correction and setup translations that move to the glyph midpoint.
535                 xOrientationShift -= scaledGlyphAdvance / 2;
536                 yOrientationShift += m_dy - baselineShift;
537             }
538
539             // Calculate current offset along path.
540             textPathOffset = m_textPathCurrentOffset + scaledGlyphAdvance / 2;
541
542             // Move to next character.
543             m_textPathCurrentOffset += scaledGlyphAdvance + m_textPathSpacing + spacing * m_textPathScaling;
544
545             // Skip character, if we're before the path.
546             if (textPathOffset < 0) {
547                 advanceToNextLogicalCharacter(logicalMetrics);
548                 advanceToNextVisualCharacter(visualMetrics);
549                 continue;
550             }
551
552             // Stop processing, if the next character lies behind the path.
553             if (textPathOffset > m_textPathLength)
554                 break;
555
556             bool ok = false;
557             FloatPoint point = m_textPath.pointAtLength(textPathOffset, ok);
558             ASSERT(ok);
559
560             x = point.x();
561             y = point.y();
562             angle = m_textPath.normalAngleAtLength(textPathOffset, ok);
563             ASSERT(ok);
564
565             // For vertical text on path, the actual angle has to be rotated 90 degrees anti-clockwise, not the orientation angle!
566             if (m_isVerticalText)
567                 angle -= 90;
568         } else {
569             // Apply all previously calculated shift values.
570             if (m_isVerticalText) {
571                 x += baselineShift;
572                 y -= kerning;
573             } else {
574                 x -= kerning;
575                 y -= baselineShift;
576             }
577
578             x += m_dx;
579             y += m_dy;
580         }
581
582         // Determine whether we have to start a new fragment.
583         bool shouldStartNewFragment = m_dx || m_dy || m_isVerticalText || m_inPathLayout || angle || angle != lastAngle
584             || orientationAngle || kerning || applySpacingToNextCharacter || definesTextLength;
585
586         // If we already started a fragment, close it now.
587         if (didStartTextFragment && shouldStartNewFragment) {
588             applySpacingToNextCharacter = false;
589             recordTextFragment(textBox, visualMetricsValues);
590         }
591
592         // Eventually start a new fragment, if not yet done.
593         if (!didStartTextFragment || shouldStartNewFragment) {
594             ASSERT(!m_currentTextFragment.characterOffset);
595             ASSERT(!m_currentTextFragment.length);
596
597             didStartTextFragment = true;
598             m_currentTextFragment.characterOffset = m_visualCharacterOffset;
599             m_currentTextFragment.metricsListOffset = m_visualMetricsListOffset;
600             m_currentTextFragment.x = x;
601             m_currentTextFragment.y = y;
602
603             // Build fragment transformation.
604             if (angle)
605                 m_currentTextFragment.transform.rotate(angle);
606
607             if (xOrientationShift || yOrientationShift)
608                 m_currentTextFragment.transform.translate(xOrientationShift, yOrientationShift);
609
610             if (orientationAngle)
611                 m_currentTextFragment.transform.rotate(orientationAngle);
612
613             m_currentTextFragment.isTextOnPath = m_inPathLayout && m_textPathScaling != 1;
614             if (m_currentTextFragment.isTextOnPath) {
615                 if (m_isVerticalText)
616                     m_currentTextFragment.lengthAdjustTransform.scaleNonUniform(1, m_textPathScaling);
617                 else
618                     m_currentTextFragment.lengthAdjustTransform.scaleNonUniform(m_textPathScaling, 1);
619             }
620         }
621
622         // Update current text position, after processing of the current character finished.
623         if (m_inPathLayout)
624             updateCurrentTextPosition(x, y, glyphAdvance);
625         else {
626             // Apply CSS 'kerning', 'letter-spacing' and 'word-spacing' to next character, if needed.
627             if (spacing)
628                 applySpacingToNextCharacter = true;
629
630             float xNew = x - m_dx;
631             float yNew = y - m_dy;
632
633             if (m_isVerticalText)
634                 xNew -= baselineShift;
635             else
636                 yNew += baselineShift;
637
638             updateCurrentTextPosition(xNew, yNew, glyphAdvance + spacing);
639         }
640
641         advanceToNextLogicalCharacter(logicalMetrics);
642         advanceToNextVisualCharacter(visualMetrics);
643         lastAngle = angle;
644     }
645
646     if (!didStartTextFragment)
647         return;
648
649     // Close last open fragment, if needed.
650     recordTextFragment(textBox, visualMetricsValues);
651 }
652
653 }