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