fix logic
[personal-kdelibs.git] / khtml / rendering / SVGCharacterLayoutInfo.cpp
blobc58c8e4bedcd9770e278d905300f29a7add44147
1 /*
2 * This file is part of the WebKit project.
4 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 #include "config.h"
24 #include "wtf/Platform.h"
26 #if ENABLE(SVG)
27 #include "SVGCharacterLayoutInfo.h"
29 //#include "InlineFlowBox.h"
30 //#include "InlineTextBox.h"
31 #include "SVGLengthList.h"
32 #include "SVGNumberList.h"
33 #include "SVGTextPositioningElement.h"
34 #include "RenderSVGTextPath.h"
36 #include <float.h>
38 #include "rendering/render_line.h"
40 namespace WebCore {
41 using namespace khtml;
43 // Helper function
44 static float calculateBaselineShift(RenderObject* item)
46 const Font& font = item->style()->htmlFont();
47 const SVGRenderStyle* svgStyle = item->style()->svgStyle();
49 float baselineShift = 0.0f;
50 if (svgStyle->baselineShift() == BS_LENGTH) {
51 CSSPrimitiveValueImpl* primitive = static_cast<CSSPrimitiveValueImpl*>(svgStyle->baselineShiftValue());
52 baselineShift = primitive->floatValue();
54 /*FIXME khtml if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
55 baselineShift = baselineShift / 100.0f * font.pixelSize();*/
56 } else {
57 float baselineAscent = font.ascent() + font.descent();
59 switch (svgStyle->baselineShift()) {
60 case BS_BASELINE:
61 break;
62 case BS_SUB:
63 baselineShift = -baselineAscent / 2.0f;
64 break;
65 case BS_SUPER:
66 baselineShift = baselineAscent / 2.0f;
67 break;
68 default:
69 ASSERT_NOT_REACHED();
73 return baselineShift;
76 SVGCharacterLayoutInfo::SVGCharacterLayoutInfo(Vector<SVGChar>& chars)
77 : curx(0.0f)
78 , cury(0.0f)
79 , angle(0.0f)
80 , dx(0.0f)
81 , dy(0.0f)
82 , shiftx(0.0f)
83 , shifty(0.0f)
84 , pathExtraAdvance(0.0f)
85 , pathTextLength(0.0f)
86 , pathChunkLength(0.0f)
87 , svgChars(chars)
88 , nextDrawnSeperated(false)
89 , xStackChanged(false)
90 , yStackChanged(false)
91 , dxStackChanged(false)
92 , dyStackChanged(false)
93 , angleStackChanged(false)
94 , baselineShiftStackChanged(false)
95 , pathLayout(false)
96 , currentOffset(0.0f)
97 , startOffset(0.0f)
98 , layoutPathLength(0.0f)
102 bool SVGCharacterLayoutInfo::xValueAvailable() const
104 return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size();
107 bool SVGCharacterLayoutInfo::yValueAvailable() const
109 return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size();
112 bool SVGCharacterLayoutInfo::dxValueAvailable() const
114 return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size();
117 bool SVGCharacterLayoutInfo::dyValueAvailable() const
119 return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size();
122 bool SVGCharacterLayoutInfo::angleValueAvailable() const
124 return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size();
127 bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const
129 return !baselineShiftStack.isEmpty();
132 float SVGCharacterLayoutInfo::xValueNext() const
134 ASSERT(!xStack.isEmpty());
135 return xStack.last().valueAtCurrentPosition();
138 float SVGCharacterLayoutInfo::yValueNext() const
140 ASSERT(!yStack.isEmpty());
141 return yStack.last().valueAtCurrentPosition();
144 float SVGCharacterLayoutInfo::dxValueNext() const
146 ASSERT(!dxStack.isEmpty());
147 return dxStack.last().valueAtCurrentPosition();
150 float SVGCharacterLayoutInfo::dyValueNext() const
152 ASSERT(!dyStack.isEmpty());
153 return dyStack.last().valueAtCurrentPosition();
156 float SVGCharacterLayoutInfo::angleValueNext() const
158 ASSERT(!angleStack.isEmpty());
159 return angleStack.last().valueAtCurrentPosition();
162 float SVGCharacterLayoutInfo::baselineShiftValueNext() const
164 ASSERT(!baselineShiftStack.isEmpty());
165 return baselineShiftStack.last();
168 void SVGCharacterLayoutInfo::processedSingleCharacter()
170 xStackWalk();
171 yStackWalk();
172 dxStackWalk();
173 dyStackWalk();
174 angleStackWalk();
175 baselineShiftStackWalk();
178 void SVGCharacterLayoutInfo::processedChunk(float savedShiftX, float savedShiftY)
180 // baseline-shift doesn't span across ancestors, unlike dx/dy.
181 curx += savedShiftX - shiftx;
182 cury += savedShiftY - shifty;
184 if (inPathLayout()) {
185 shiftx = savedShiftX;
186 shifty = savedShiftY;
189 // rotation also doesn't span
190 angle = 0.0f;
192 if (xStackChanged) {
193 ASSERT(!xStack.isEmpty());
194 xStack.removeLast();
195 xStackChanged = false;
198 if (yStackChanged) {
199 ASSERT(!yStack.isEmpty());
200 yStack.removeLast();
201 yStackChanged = false;
204 if (dxStackChanged) {
205 ASSERT(!dxStack.isEmpty());
206 dxStack.removeLast();
207 dxStackChanged = false;
210 if (dyStackChanged) {
211 ASSERT(!dyStack.isEmpty());
212 dyStack.removeLast();
213 dyStackChanged = false;
216 if (angleStackChanged) {
217 ASSERT(!angleStack.isEmpty());
218 angleStack.removeLast();
219 angleStackChanged = false;
222 if (baselineShiftStackChanged) {
223 ASSERT(!baselineShiftStack.isEmpty());
224 baselineShiftStack.removeLast();
225 baselineShiftStackChanged = false;
229 bool SVGCharacterLayoutInfo::nextPathLayoutPointAndAngle(float glyphAdvance, float extraAdvance, float newOffset)
231 if (layoutPathLength <= 0.0f)
232 return false;
234 if (newOffset != FLT_MIN)
235 currentOffset = startOffset + newOffset;
237 // Respect translation along path (extraAdvance is orthogonal to the path)
238 currentOffset += extraAdvance;
240 float offset = currentOffset + glyphAdvance / 2.0f;
241 currentOffset += glyphAdvance + pathExtraAdvance;
243 if (offset < 0.0f || offset > layoutPathLength)
244 return false;
246 bool ok = false;
247 FloatPoint point = layoutPath.pointAtLength(offset, ok);
248 ASSERT(ok);
250 curx = point.x();
251 cury = point.y();
253 angle = layoutPath.normalAngleAtLength(offset, ok);
254 ASSERT(ok);
256 // fprintf(stderr, "t: %f, x: %f, y: %f, angle: %f, glyphAdvance: %f\n", currentOffset, x, y, angle, glyphAdvance);
257 return true;
260 bool SVGCharacterLayoutInfo::inPathLayout() const
262 return pathLayout;
265 void SVGCharacterLayoutInfo::setInPathLayout(bool value)
267 pathLayout = value;
269 pathExtraAdvance = 0.0f;
270 pathTextLength = 0.0f;
271 pathChunkLength = 0.0f;
274 void SVGCharacterLayoutInfo::addLayoutInformation(InlineFlowBox* flowBox, float textAnchorStartOffset)
276 bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() &&
277 dxStack.isEmpty() && dyStack.isEmpty() &&
278 angleStack.isEmpty() && baselineShiftStack.isEmpty() &&
279 curx == 0.0f && cury == 0.0f;
281 RenderSVGTextPath* textPath = static_cast<RenderSVGTextPath*>(flowBox->object());
282 Path path = textPath->layoutPath();
284 float baselineShift = calculateBaselineShift(textPath);
286 layoutPath = path;
287 layoutPathLength = path.length();
289 if (layoutPathLength <= 0.0f)
290 return;
292 startOffset = textPath->startOffset();
294 if (textPath->startOffset() >= 0.0f && textPath->startOffset() <= 1.0f)
295 startOffset *= layoutPathLength;
297 startOffset += textAnchorStartOffset;
298 currentOffset = startOffset;
300 // Only baseline-shift is handled through the normal layout system
301 addStackContent(BaselineShiftStack, baselineShift);
303 if (isInitialLayout) {
304 xStackChanged = false;
305 yStackChanged = false;
306 dxStackChanged = false;
307 dyStackChanged = false;
308 angleStackChanged = false;
309 baselineShiftStackChanged = false;
313 void SVGCharacterLayoutInfo::addLayoutInformation(SVGTextPositioningElement* element)
315 bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() &&
316 dxStack.isEmpty() && dyStack.isEmpty() &&
317 angleStack.isEmpty() && baselineShiftStack.isEmpty() &&
318 curx == 0.0f && cury == 0.0f;
320 float baselineShift = calculateBaselineShift(element->renderer());
322 addStackContent(XStack, element->x());
323 addStackContent(YStack, element->y());
324 addStackContent(DxStack, element->dx());
325 addStackContent(DyStack, element->dy());
326 addStackContent(AngleStack, element->rotate());
327 addStackContent(BaselineShiftStack, baselineShift);
329 if (isInitialLayout) {
330 xStackChanged = false;
331 yStackChanged = false;
332 dxStackChanged = false;
333 dyStackChanged = false;
334 angleStackChanged = false;
335 baselineShiftStackChanged = false;
339 void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGNumberList* list)
341 unsigned length = list->numberOfItems();
342 if (!length)
343 return;
345 PositionedFloatVector newLayoutInfo;
347 // TODO: Convert more efficiently!
348 ExceptionCode ec = 0;
349 for (unsigned i = 0; i < length; ++i) {
350 float value = list->getItem(i, ec);
351 ASSERT(ec == 0);
353 newLayoutInfo.append(value);
356 addStackContent(type, newLayoutInfo);
359 void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGLengthList* list)
361 unsigned length = list->numberOfItems();
362 if (!length)
363 return;
365 PositionedFloatVector newLayoutInfo;
367 ExceptionCode ec = 0;
368 for (unsigned i = 0; i < length; ++i) {
369 float value = list->getItem(i, ec).value();
370 ASSERT(ec == 0);
372 newLayoutInfo.append(value);
375 addStackContent(type, newLayoutInfo);
378 void SVGCharacterLayoutInfo::addStackContent(StackType type, const PositionedFloatVector& list)
380 switch (type) {
381 case XStack:
382 xStackChanged = true;
383 xStack.append(list);
384 break;
385 case YStack:
386 yStackChanged = true;
387 yStack.append(list);
388 break;
389 case DxStack:
390 dxStackChanged = true;
391 dxStack.append(list);
392 break;
393 case DyStack:
394 dyStackChanged = true;
395 dyStack.append(list);
396 break;
397 case AngleStack:
398 angleStackChanged = true;
399 angleStack.append(list);
400 break;
401 default:
402 ASSERT_NOT_REACHED();
406 void SVGCharacterLayoutInfo::addStackContent(StackType type, float value)
408 if (value == 0.0f)
409 return;
411 switch (type) {
412 case BaselineShiftStack:
413 baselineShiftStackChanged = true;
414 baselineShiftStack.append(value);
415 break;
416 default:
417 ASSERT_NOT_REACHED();
421 void SVGCharacterLayoutInfo::xStackWalk()
423 unsigned i = 1;
425 while (!xStack.isEmpty()) {
426 PositionedFloatVector& cur = xStack.last();
427 if (i + cur.position() < cur.size()) {
428 cur.advance(i);
429 break;
432 i += cur.position();
433 xStack.removeLast();
434 xStackChanged = false;
438 void SVGCharacterLayoutInfo::yStackWalk()
440 unsigned i = 1;
442 while (!yStack.isEmpty()) {
443 PositionedFloatVector& cur = yStack.last();
444 if (i + cur.position() < cur.size()) {
445 cur.advance(i);
446 break;
449 i += cur.position();
450 yStack.removeLast();
451 yStackChanged = false;
455 void SVGCharacterLayoutInfo::dxStackWalk()
457 unsigned i = 1;
459 while (!dxStack.isEmpty()) {
460 PositionedFloatVector& cur = dxStack.last();
461 if (i + cur.position() < cur.size()) {
462 cur.advance(i);
463 break;
466 i += cur.position();
467 dxStack.removeLast();
468 dxStackChanged = false;
472 void SVGCharacterLayoutInfo::dyStackWalk()
474 unsigned i = 1;
476 while (!dyStack.isEmpty()) {
477 PositionedFloatVector& cur = dyStack.last();
478 if (i + cur.position() < cur.size()) {
479 cur.advance(i);
480 break;
483 i += cur.position();
484 dyStack.removeLast();
485 dyStackChanged = false;
489 void SVGCharacterLayoutInfo::angleStackWalk()
491 unsigned i = 1;
493 while (!angleStack.isEmpty()) {
494 PositionedFloatVector& cur = angleStack.last();
495 if (i + cur.position() < cur.size()) {
496 cur.advance(i);
497 break;
500 i += cur.position();
501 angleStack.removeLast();
502 angleStackChanged = false;
506 void SVGCharacterLayoutInfo::baselineShiftStackWalk()
508 if (!baselineShiftStack.isEmpty()) {
509 baselineShiftStack.removeLast();
510 baselineShiftStackChanged = false;
514 bool SVGChar::isHidden() const
516 return pathData && pathData->hidden;
519 AffineTransform SVGChar::characterTransform() const
521 AffineTransform ctm;
523 // Rotate character around angle, and possibly scale.
524 ctm.translate(x, y);
525 ctm.rotate(angle);
527 if (pathData) {
528 ctm.scale(pathData->xScale, pathData->yScale);
529 ctm.translate(pathData->xShift, pathData->yShift);
530 ctm.rotate(pathData->orientationAngle);
533 ctm.translate(orientationShiftX - x, orientationShiftY - y);
534 return ctm;
537 } // namespace WebCore
539 #endif // ENABLE(SVG)