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.
24 #include "wtf/Platform.h"
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"
38 #include "rendering/render_line.h"
41 using namespace khtml
;
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();*/
57 float baselineAscent
= font
.ascent() + font
.descent();
59 switch (svgStyle
->baselineShift()) {
63 baselineShift
= -baselineAscent
/ 2.0f
;
66 baselineShift
= baselineAscent
/ 2.0f
;
76 SVGCharacterLayoutInfo::SVGCharacterLayoutInfo(Vector
<SVGChar
>& chars
)
84 , pathExtraAdvance(0.0f
)
85 , pathTextLength(0.0f
)
86 , pathChunkLength(0.0f
)
88 , nextDrawnSeperated(false)
89 , xStackChanged(false)
90 , yStackChanged(false)
91 , dxStackChanged(false)
92 , dyStackChanged(false)
93 , angleStackChanged(false)
94 , baselineShiftStackChanged(false)
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()
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
193 ASSERT(!xStack
.isEmpty());
195 xStackChanged
= false;
199 ASSERT(!yStack
.isEmpty());
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
)
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
)
247 FloatPoint point
= layoutPath
.pointAtLength(offset
, ok
);
253 angle
= layoutPath
.normalAngleAtLength(offset
, ok
);
256 // fprintf(stderr, "t: %f, x: %f, y: %f, angle: %f, glyphAdvance: %f\n", currentOffset, x, y, angle, glyphAdvance);
260 bool SVGCharacterLayoutInfo::inPathLayout() const
265 void SVGCharacterLayoutInfo::setInPathLayout(bool 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
);
287 layoutPathLength
= path
.length();
289 if (layoutPathLength
<= 0.0f
)
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();
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
);
353 newLayoutInfo
.append(value
);
356 addStackContent(type
, newLayoutInfo
);
359 void SVGCharacterLayoutInfo::addStackContent(StackType type
, SVGLengthList
* list
)
361 unsigned length
= list
->numberOfItems();
365 PositionedFloatVector newLayoutInfo
;
367 ExceptionCode ec
= 0;
368 for (unsigned i
= 0; i
< length
; ++i
) {
369 float value
= list
->getItem(i
, ec
).value();
372 newLayoutInfo
.append(value
);
375 addStackContent(type
, newLayoutInfo
);
378 void SVGCharacterLayoutInfo::addStackContent(StackType type
, const PositionedFloatVector
& list
)
382 xStackChanged
= true;
386 yStackChanged
= true;
390 dxStackChanged
= true;
391 dxStack
.append(list
);
394 dyStackChanged
= true;
395 dyStack
.append(list
);
398 angleStackChanged
= true;
399 angleStack
.append(list
);
402 ASSERT_NOT_REACHED();
406 void SVGCharacterLayoutInfo::addStackContent(StackType type
, float value
)
412 case BaselineShiftStack
:
413 baselineShiftStackChanged
= true;
414 baselineShiftStack
.append(value
);
417 ASSERT_NOT_REACHED();
421 void SVGCharacterLayoutInfo::xStackWalk()
425 while (!xStack
.isEmpty()) {
426 PositionedFloatVector
& cur
= xStack
.last();
427 if (i
+ cur
.position() < cur
.size()) {
434 xStackChanged
= false;
438 void SVGCharacterLayoutInfo::yStackWalk()
442 while (!yStack
.isEmpty()) {
443 PositionedFloatVector
& cur
= yStack
.last();
444 if (i
+ cur
.position() < cur
.size()) {
451 yStackChanged
= false;
455 void SVGCharacterLayoutInfo::dxStackWalk()
459 while (!dxStack
.isEmpty()) {
460 PositionedFloatVector
& cur
= dxStack
.last();
461 if (i
+ cur
.position() < cur
.size()) {
467 dxStack
.removeLast();
468 dxStackChanged
= false;
472 void SVGCharacterLayoutInfo::dyStackWalk()
476 while (!dyStack
.isEmpty()) {
477 PositionedFloatVector
& cur
= dyStack
.last();
478 if (i
+ cur
.position() < cur
.size()) {
484 dyStack
.removeLast();
485 dyStackChanged
= false;
489 void SVGCharacterLayoutInfo::angleStackWalk()
493 while (!angleStack
.isEmpty()) {
494 PositionedFloatVector
& cur
= angleStack
.last();
495 if (i
+ cur
.position() < cur
.size()) {
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
523 // Rotate character around angle, and possibly scale.
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
);
537 } // namespace WebCore
539 #endif // ENABLE(SVG)