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