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