Reduce use of deprecatedCharacters in WebCore
[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 = toRenderSVGTextPath(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     SVGElement* lengthContext = toSVGElement(text->parent()->element());
427     
428     RenderObject* textParent = text->parent();
429     bool definesTextLength = textParent ? parentDefinesTextLength(textParent) : false;
430
431     const SVGRenderStyle& svgStyle = style->svgStyle();
432
433     m_visualMetricsListOffset = 0;
434     m_visualCharacterOffset = 0;
435
436     Vector<SVGTextMetrics>& visualMetricsValues = text->layoutAttributes()->textMetricsValues();
437     ASSERT(!visualMetricsValues.isEmpty());
438
439     auto upconvertedCharacters = StringView(text->text()).upconvertedCharacters();
440     const UChar* characters = upconvertedCharacters;
441     const Font& font = style->font();
442
443     SVGTextLayoutEngineSpacing spacingLayout(font);
444     SVGTextLayoutEngineBaseline baselineLayout(font);
445
446     bool didStartTextFragment = false;
447     bool applySpacingToNextCharacter = false;
448
449     float lastAngle = 0;
450     float baselineShift = baselineLayout.calculateBaselineShift(&svgStyle, lengthContext);
451     baselineShift -= baselineLayout.calculateAlignmentBaselineShift(m_isVerticalText, text);
452
453     // Main layout algorithm.
454     while (true) {
455         // Find the start of the current text box in this list, respecting ligatures.
456         SVGTextMetrics visualMetrics(SVGTextMetrics::SkippedSpaceMetrics);
457         if (!currentVisualCharacterMetrics(textBox, visualMetricsValues, visualMetrics))
458             break;
459
460         if (visualMetrics.isEmpty()) {
461             advanceToNextVisualCharacter(visualMetrics);
462             continue;
463         }
464
465         SVGTextLayoutAttributes* logicalAttributes = 0;
466         if (!currentLogicalCharacterAttributes(logicalAttributes))
467             break;
468
469         ASSERT(logicalAttributes);
470         SVGTextMetrics logicalMetrics(SVGTextMetrics::SkippedSpaceMetrics);
471         if (!currentLogicalCharacterMetrics(logicalAttributes, logicalMetrics))
472             break;
473
474         SVGCharacterDataMap& characterDataMap = logicalAttributes->characterDataMap();
475         SVGCharacterData data;
476         SVGCharacterDataMap::iterator it = characterDataMap.find(m_logicalCharacterOffset + 1);
477         if (it != characterDataMap.end())
478             data = it->value;
479
480         float x = data.x;
481         float y = data.y;
482
483         // When we've advanced to the box start offset, determine using the original x/y values,
484         // whether this character starts a new text chunk, before doing any further processing.
485         if (m_visualCharacterOffset == textBox->start())
486             textBox->setStartsNewTextChunk(logicalAttributes->context().characterStartsNewTextChunk(m_logicalCharacterOffset));
487
488         float angle = data.rotate == SVGTextLayoutAttributes::emptyValue() ? 0 : data.rotate;
489
490         // Calculate glyph orientation angle.
491         const UChar* currentCharacter = characters + m_visualCharacterOffset;
492         float orientationAngle = baselineLayout.calculateGlyphOrientationAngle(m_isVerticalText, &svgStyle, *currentCharacter);
493
494         // Calculate glyph advance & x/y orientation shifts.
495         float xOrientationShift = 0;
496         float yOrientationShift = 0;
497         float glyphAdvance = baselineLayout.calculateGlyphAdvanceAndOrientation(m_isVerticalText, visualMetrics, orientationAngle, xOrientationShift, yOrientationShift);
498
499         // Assign current text position to x/y values, if needed.
500         updateCharacerPositionIfNeeded(x, y);
501
502         // Apply dx/dy value adjustments to current text position, if needed.
503         updateRelativePositionAdjustmentsIfNeeded(data.dx, data.dy);
504
505         // Calculate SVG Fonts kerning, if needed.
506         float kerning = spacingLayout.calculateSVGKerning(m_isVerticalText, visualMetrics.glyph());
507
508         // Calculate CSS 'kerning', 'letter-spacing' and 'word-spacing' for next character, if needed.
509         float spacing = spacingLayout.calculateCSSKerningAndSpacing(&svgStyle, lengthContext, currentCharacter);
510
511         float textPathOffset = 0;
512         if (m_inPathLayout) {
513             float scaledGlyphAdvance = glyphAdvance * m_textPathScaling;
514             if (m_isVerticalText) {
515                 // If there's an absolute y position available, it marks the beginning of a new position along the path.
516                 if (y != SVGTextLayoutAttributes::emptyValue())
517                     m_textPathCurrentOffset = y + m_textPathStartOffset;
518
519                 m_textPathCurrentOffset += m_dy - kerning;
520                 m_dy = 0;
521
522                 // Apply dx/dy correction and setup translations that move to the glyph midpoint.
523                 xOrientationShift += m_dx + baselineShift;
524                 yOrientationShift -= scaledGlyphAdvance / 2;
525             } else {
526                 // If there's an absolute x position available, it marks the beginning of a new position along the path.
527                 if (x != SVGTextLayoutAttributes::emptyValue())
528                     m_textPathCurrentOffset = x + m_textPathStartOffset;
529
530                 m_textPathCurrentOffset += m_dx - kerning;
531                 m_dx = 0;
532
533                 // Apply dx/dy correction and setup translations that move to the glyph midpoint.
534                 xOrientationShift -= scaledGlyphAdvance / 2;
535                 yOrientationShift += m_dy - baselineShift;
536             }
537
538             // Calculate current offset along path.
539             textPathOffset = m_textPathCurrentOffset + scaledGlyphAdvance / 2;
540
541             // Move to next character.
542             m_textPathCurrentOffset += scaledGlyphAdvance + m_textPathSpacing + spacing * m_textPathScaling;
543
544             // Skip character, if we're before the path.
545             if (textPathOffset < 0) {
546                 advanceToNextLogicalCharacter(logicalMetrics);
547                 advanceToNextVisualCharacter(visualMetrics);
548                 continue;
549             }
550
551             // Stop processing, if the next character lies behind the path.
552             if (textPathOffset > m_textPathLength)
553                 break;
554
555             bool ok = false;
556             FloatPoint point = m_textPath.pointAtLength(textPathOffset, ok);
557             ASSERT(ok);
558
559             x = point.x();
560             y = point.y();
561             angle = m_textPath.normalAngleAtLength(textPathOffset, ok);
562             ASSERT(ok);
563
564             // For vertical text on path, the actual angle has to be rotated 90 degrees anti-clockwise, not the orientation angle!
565             if (m_isVerticalText)
566                 angle -= 90;
567         } else {
568             // Apply all previously calculated shift values.
569             if (m_isVerticalText) {
570                 x += baselineShift;
571                 y -= kerning;
572             } else {
573                 x -= kerning;
574                 y -= baselineShift;
575             }
576
577             x += m_dx;
578             y += m_dy;
579         }
580
581         // Determine whether we have to start a new fragment.
582         bool shouldStartNewFragment = m_dx || m_dy || m_isVerticalText || m_inPathLayout || angle || angle != lastAngle
583             || orientationAngle || kerning || applySpacingToNextCharacter || definesTextLength;
584
585         // If we already started a fragment, close it now.
586         if (didStartTextFragment && shouldStartNewFragment) {
587             applySpacingToNextCharacter = false;
588             recordTextFragment(textBox, visualMetricsValues);
589         }
590
591         // Eventually start a new fragment, if not yet done.
592         if (!didStartTextFragment || shouldStartNewFragment) {
593             ASSERT(!m_currentTextFragment.characterOffset);
594             ASSERT(!m_currentTextFragment.length);
595
596             didStartTextFragment = true;
597             m_currentTextFragment.characterOffset = m_visualCharacterOffset;
598             m_currentTextFragment.metricsListOffset = m_visualMetricsListOffset;
599             m_currentTextFragment.x = x;
600             m_currentTextFragment.y = y;
601
602             // Build fragment transformation.
603             if (angle)
604                 m_currentTextFragment.transform.rotate(angle);
605
606             if (xOrientationShift || yOrientationShift)
607                 m_currentTextFragment.transform.translate(xOrientationShift, yOrientationShift);
608
609             if (orientationAngle)
610                 m_currentTextFragment.transform.rotate(orientationAngle);
611
612             m_currentTextFragment.isTextOnPath = m_inPathLayout && m_textPathScaling != 1;
613             if (m_currentTextFragment.isTextOnPath) {
614                 if (m_isVerticalText)
615                     m_currentTextFragment.lengthAdjustTransform.scaleNonUniform(1, m_textPathScaling);
616                 else
617                     m_currentTextFragment.lengthAdjustTransform.scaleNonUniform(m_textPathScaling, 1);
618             }
619         }
620
621         // Update current text position, after processing of the current character finished.
622         if (m_inPathLayout)
623             updateCurrentTextPosition(x, y, glyphAdvance);
624         else {
625             // Apply CSS 'kerning', 'letter-spacing' and 'word-spacing' to next character, if needed.
626             if (spacing)
627                 applySpacingToNextCharacter = true;
628
629             float xNew = x - m_dx;
630             float yNew = y - m_dy;
631
632             if (m_isVerticalText)
633                 xNew -= baselineShift;
634             else
635                 yNew += baselineShift;
636
637             updateCurrentTextPosition(xNew, yNew, glyphAdvance + spacing);
638         }
639
640         advanceToNextLogicalCharacter(logicalMetrics);
641         advanceToNextVisualCharacter(visualMetrics);
642         lastAngle = angle;
643     }
644
645     if (!didStartTextFragment)
646         return;
647
648     // Close last open fragment, if needed.
649     recordTextFragment(textBox, visualMetricsValues);
650 }
651
652 }