1 /*
2 * This file is part of the WebKit project.
3 *
4 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
5 *
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.
10 *
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.
15 *
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.
20 *
21 */
22
23 #include "config.h"
24
25 #if ENABLE(SVG)
26 #include "SVGCharacterLayoutInfo.h"
27
28 #include "InlineFlowBox.h"
29 #include "InlineTextBox.h"
30 #include "SVGLengthList.h"
31 #include "SVGNumberList.h"
32 #include "SVGTextPositioningElement.h"
33 #include "RenderSVGTextPath.h"
34
35 #include <float.h>
36
37 namespace WebCore {
38
39 // Helper function
calculateBaselineShift(RenderObject * item)40 static float calculateBaselineShift(RenderObject* item)
41 {
42 const Font& font = item->style()->font();
43 const SVGRenderStyle* svgStyle = item->style()->svgStyle();
44
45 float baselineShift = 0.0f;
46 if (svgStyle->baselineShift() == BS_LENGTH) {
47 CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->baselineShiftValue());
48 baselineShift = primitive->getFloatValue();
49
50 if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
51 baselineShift = baselineShift / 100.0f * font.pixelSize();
52 } else {
53 float baselineAscent = font.ascent() + font.descent();
54
55 switch (svgStyle->baselineShift()) {
56 case BS_BASELINE:
57 break;
58 case BS_SUB:
59 baselineShift = -baselineAscent / 2.0f;
60 break;
61 case BS_SUPER:
62 baselineShift = baselineAscent / 2.0f;
63 break;
64 default:
65 ASSERT_NOT_REACHED();
66 }
67 }
68
69 return baselineShift;
70 }
71
SVGCharacterLayoutInfo(Vector<SVGChar> & chars)72 SVGCharacterLayoutInfo::SVGCharacterLayoutInfo(Vector<SVGChar>& chars)
73 : curx(0.0f)
74 , cury(0.0f)
75 , angle(0.0f)
76 , dx(0.0f)
77 , dy(0.0f)
78 , shiftx(0.0f)
79 , shifty(0.0f)
80 , pathExtraAdvance(0.0f)
81 , pathTextLength(0.0f)
82 , pathChunkLength(0.0f)
83 , svgChars(chars)
84 , nextDrawnSeperated(false)
85 , xStackChanged(false)
86 , yStackChanged(false)
87 , dxStackChanged(false)
88 , dyStackChanged(false)
89 , angleStackChanged(false)
90 , baselineShiftStackChanged(false)
91 , pathLayout(false)
92 , currentOffset(0.0f)
93 , startOffset(0.0f)
94 , layoutPathLength(0.0f)
95 {
96 }
97
xValueAvailable() const98 bool SVGCharacterLayoutInfo::xValueAvailable() const
99 {
100 return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size();
101 }
102
yValueAvailable() const103 bool SVGCharacterLayoutInfo::yValueAvailable() const
104 {
105 return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size();
106 }
107
dxValueAvailable() const108 bool SVGCharacterLayoutInfo::dxValueAvailable() const
109 {
110 return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size();
111 }
112
dyValueAvailable() const113 bool SVGCharacterLayoutInfo::dyValueAvailable() const
114 {
115 return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size();
116 }
117
angleValueAvailable() const118 bool SVGCharacterLayoutInfo::angleValueAvailable() const
119 {
120 return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size();
121 }
122
baselineShiftValueAvailable() const123 bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const
124 {
125 return !baselineShiftStack.isEmpty();
126 }
127
xValueNext() const128 float SVGCharacterLayoutInfo::xValueNext() const
129 {
130 ASSERT(!xStack.isEmpty());
131 return xStack.last().valueAtCurrentPosition();
132 }
133
yValueNext() const134 float SVGCharacterLayoutInfo::yValueNext() const
135 {
136 ASSERT(!yStack.isEmpty());
137 return yStack.last().valueAtCurrentPosition();
138 }
139
dxValueNext() const140 float SVGCharacterLayoutInfo::dxValueNext() const
141 {
142 ASSERT(!dxStack.isEmpty());
143 return dxStack.last().valueAtCurrentPosition();
144 }
145
dyValueNext() const146 float SVGCharacterLayoutInfo::dyValueNext() const
147 {
148 ASSERT(!dyStack.isEmpty());
149 return dyStack.last().valueAtCurrentPosition();
150 }
151
angleValueNext() const152 float SVGCharacterLayoutInfo::angleValueNext() const
153 {
154 ASSERT(!angleStack.isEmpty());
155 return angleStack.last().valueAtCurrentPosition();
156 }
157
baselineShiftValueNext() const158 float SVGCharacterLayoutInfo::baselineShiftValueNext() const
159 {
160 ASSERT(!baselineShiftStack.isEmpty());
161 return baselineShiftStack.last();
162 }
163
processedSingleCharacter()164 void SVGCharacterLayoutInfo::processedSingleCharacter()
165 {
166 xStackWalk();
167 yStackWalk();
168 dxStackWalk();
169 dyStackWalk();
170 angleStackWalk();
171 baselineShiftStackWalk();
172 }
173
processedChunk(float savedShiftX,float savedShiftY)174 void SVGCharacterLayoutInfo::processedChunk(float savedShiftX, float savedShiftY)
175 {
176 // baseline-shift doesn't span across ancestors, unlike dx/dy.
177 curx += savedShiftX - shiftx;
178 cury += savedShiftY - shifty;
179
180 if (inPathLayout()) {
181 shiftx = savedShiftX;
182 shifty = savedShiftY;
183 }
184
185 // rotation also doesn't span
186 angle = 0.0f;
187
188 if (xStackChanged) {
189 ASSERT(!xStack.isEmpty());
190 xStack.removeLast();
191 xStackChanged = false;
192 }
193
194 if (yStackChanged) {
195 ASSERT(!yStack.isEmpty());
196 yStack.removeLast();
197 yStackChanged = false;
198 }
199
200 if (dxStackChanged) {
201 ASSERT(!dxStack.isEmpty());
202 dxStack.removeLast();
203 dxStackChanged = false;
204 }
205
206 if (dyStackChanged) {
207 ASSERT(!dyStack.isEmpty());
208 dyStack.removeLast();
209 dyStackChanged = false;
210 }
211
212 if (angleStackChanged) {
213 ASSERT(!angleStack.isEmpty());
214 angleStack.removeLast();
215 angleStackChanged = false;
216 }
217
218 if (baselineShiftStackChanged) {
219 ASSERT(!baselineShiftStack.isEmpty());
220 baselineShiftStack.removeLast();
221 baselineShiftStackChanged = false;
222 }
223 }
224
nextPathLayoutPointAndAngle(float glyphAdvance,float extraAdvance,float newOffset)225 bool SVGCharacterLayoutInfo::nextPathLayoutPointAndAngle(float glyphAdvance, float extraAdvance, float newOffset)
226 {
227 if (layoutPathLength <= 0.0f)
228 return false;
229
230 if (newOffset != FLT_MIN)
231 currentOffset = startOffset + newOffset;
232
233 // Respect translation along path (extraAdvance is orthogonal to the path)
234 currentOffset += extraAdvance;
235
236 float offset = currentOffset + glyphAdvance / 2.0f;
237 currentOffset += glyphAdvance + pathExtraAdvance;
238
239 if (offset < 0.0f || offset > layoutPathLength)
240 return false;
241
242 bool ok = false;
243 FloatPoint point = layoutPath.pointAtLength(offset, ok);
244 ASSERT(ok);
245
246 curx = point.x();
247 cury = point.y();
248
249 angle = layoutPath.normalAngleAtLength(offset, ok);
250 ASSERT(ok);
251
252 // fprintf(stderr, "t: %f, x: %f, y: %f, angle: %f, glyphAdvance: %f\n", currentOffset, x, y, angle, glyphAdvance);
253 return true;
254 }
255
inPathLayout() const256 bool SVGCharacterLayoutInfo::inPathLayout() const
257 {
258 return pathLayout;
259 }
260
setInPathLayout(bool value)261 void SVGCharacterLayoutInfo::setInPathLayout(bool value)
262 {
263 pathLayout = value;
264
265 pathExtraAdvance = 0.0f;
266 pathTextLength = 0.0f;
267 pathChunkLength = 0.0f;
268 }
269
addLayoutInformation(InlineFlowBox * flowBox,float textAnchorStartOffset)270 void SVGCharacterLayoutInfo::addLayoutInformation(InlineFlowBox* flowBox, float textAnchorStartOffset)
271 {
272 bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() &&
273 dxStack.isEmpty() && dyStack.isEmpty() &&
274 angleStack.isEmpty() && baselineShiftStack.isEmpty() &&
275 curx == 0.0f && cury == 0.0f;
276
277 RenderSVGTextPath* textPath = toRenderSVGTextPath(flowBox->renderer());
278 Path path = textPath->layoutPath();
279
280 float baselineShift = calculateBaselineShift(textPath);
281
282 layoutPath = path;
283 layoutPathLength = path.length();
284
285 if (layoutPathLength <= 0.0f)
286 return;
287
288 startOffset = textPath->startOffset();
289
290 if (textPath->startOffset() >= 0.0f && textPath->startOffset() <= 1.0f)
291 startOffset *= layoutPathLength;
292
293 startOffset += textAnchorStartOffset;
294 currentOffset = startOffset;
295
296 // Only baseline-shift is handled through the normal layout system
297 addStackContent(BaselineShiftStack, baselineShift);
298
299 if (isInitialLayout) {
300 xStackChanged = false;
301 yStackChanged = false;
302 dxStackChanged = false;
303 dyStackChanged = false;
304 angleStackChanged = false;
305 baselineShiftStackChanged = false;
306 }
307 }
308
addLayoutInformation(SVGTextPositioningElement * element)309 void SVGCharacterLayoutInfo::addLayoutInformation(SVGTextPositioningElement* element)
310 {
311 bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() &&
312 dxStack.isEmpty() && dyStack.isEmpty() &&
313 angleStack.isEmpty() && baselineShiftStack.isEmpty() &&
314 curx == 0.0f && cury == 0.0f;
315
316 float baselineShift = calculateBaselineShift(element->renderer());
317
318 addStackContent(XStack, element->x(), element);
319 addStackContent(YStack, element->y(), element);
320 addStackContent(DxStack, element->dx(), element);
321 addStackContent(DyStack, element->dy(), element);
322 addStackContent(AngleStack, element->rotate());
323 addStackContent(BaselineShiftStack, baselineShift);
324
325 if (isInitialLayout) {
326 xStackChanged = false;
327 yStackChanged = false;
328 dxStackChanged = false;
329 dyStackChanged = false;
330 angleStackChanged = false;
331 baselineShiftStackChanged = false;
332 }
333 }
334
addStackContent(StackType type,SVGNumberList * list)335 void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGNumberList* list)
336 {
337 unsigned length = list->numberOfItems();
338 if (!length)
339 return;
340
341 PositionedFloatVector newLayoutInfo;
342
343 // TODO: Convert more efficiently!
344 ExceptionCode ec = 0;
345 for (unsigned i = 0; i < length; ++i) {
346 float value = list->getItem(i, ec);
347 ASSERT(ec == 0);
348
349 newLayoutInfo.append(value);
350 }
351
352 addStackContent(type, newLayoutInfo);
353 }
354
addStackContent(StackType type,SVGLengthList * list,const SVGElement * context)355 void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGLengthList* list, const SVGElement* context)
356 {
357 unsigned length = list->numberOfItems();
358 if (!length)
359 return;
360
361 PositionedFloatVector newLayoutInfo;
362
363 ExceptionCode ec = 0;
364 for (unsigned i = 0; i < length; ++i) {
365 float value = list->getItem(i, ec).value(context);
366 ASSERT(ec == 0);
367
368 newLayoutInfo.append(value);
369 }
370
371 addStackContent(type, newLayoutInfo);
372 }
373
addStackContent(StackType type,const PositionedFloatVector & list)374 void SVGCharacterLayoutInfo::addStackContent(StackType type, const PositionedFloatVector& list)
375 {
376 switch (type) {
377 case XStack:
378 xStackChanged = true;
379 xStack.append(list);
380 break;
381 case YStack:
382 yStackChanged = true;
383 yStack.append(list);
384 break;
385 case DxStack:
386 dxStackChanged = true;
387 dxStack.append(list);
388 break;
389 case DyStack:
390 dyStackChanged = true;
391 dyStack.append(list);
392 break;
393 case AngleStack:
394 angleStackChanged = true;
395 angleStack.append(list);
396 break;
397 default:
398 ASSERT_NOT_REACHED();
399 }
400 }
401
addStackContent(StackType type,float value)402 void SVGCharacterLayoutInfo::addStackContent(StackType type, float value)
403 {
404 if (value == 0.0f)
405 return;
406
407 switch (type) {
408 case BaselineShiftStack:
409 baselineShiftStackChanged = true;
410 baselineShiftStack.append(value);
411 break;
412 default:
413 ASSERT_NOT_REACHED();
414 }
415 }
416
xStackWalk()417 void SVGCharacterLayoutInfo::xStackWalk()
418 {
419 unsigned i = 1;
420
421 while (!xStack.isEmpty()) {
422 PositionedFloatVector& cur = xStack.last();
423 if (i + cur.position() < cur.size()) {
424 cur.advance(i);
425 break;
426 }
427
428 i += cur.position();
429 xStack.removeLast();
430 xStackChanged = false;
431 }
432 }
433
yStackWalk()434 void SVGCharacterLayoutInfo::yStackWalk()
435 {
436 unsigned i = 1;
437
438 while (!yStack.isEmpty()) {
439 PositionedFloatVector& cur = yStack.last();
440 if (i + cur.position() < cur.size()) {
441 cur.advance(i);
442 break;
443 }
444
445 i += cur.position();
446 yStack.removeLast();
447 yStackChanged = false;
448 }
449 }
450
dxStackWalk()451 void SVGCharacterLayoutInfo::dxStackWalk()
452 {
453 unsigned i = 1;
454
455 while (!dxStack.isEmpty()) {
456 PositionedFloatVector& cur = dxStack.last();
457 if (i + cur.position() < cur.size()) {
458 cur.advance(i);
459 break;
460 }
461
462 i += cur.position();
463 dxStack.removeLast();
464 dxStackChanged = false;
465 }
466 }
467
dyStackWalk()468 void SVGCharacterLayoutInfo::dyStackWalk()
469 {
470 unsigned i = 1;
471
472 while (!dyStack.isEmpty()) {
473 PositionedFloatVector& cur = dyStack.last();
474 if (i + cur.position() < cur.size()) {
475 cur.advance(i);
476 break;
477 }
478
479 i += cur.position();
480 dyStack.removeLast();
481 dyStackChanged = false;
482 }
483 }
484
angleStackWalk()485 void SVGCharacterLayoutInfo::angleStackWalk()
486 {
487 unsigned i = 1;
488
489 while (!angleStack.isEmpty()) {
490 PositionedFloatVector& cur = angleStack.last();
491 if (i + cur.position() < cur.size()) {
492 cur.advance(i);
493 break;
494 }
495
496 i += cur.position();
497 angleStack.removeLast();
498 angleStackChanged = false;
499 }
500 }
501
baselineShiftStackWalk()502 void SVGCharacterLayoutInfo::baselineShiftStackWalk()
503 {
504 if (!baselineShiftStack.isEmpty()) {
505 baselineShiftStack.removeLast();
506 baselineShiftStackChanged = false;
507 }
508 }
509
isHidden() const510 bool SVGChar::isHidden() const
511 {
512 return pathData && pathData->hidden;
513 }
514
characterTransform() const515 TransformationMatrix SVGChar::characterTransform() const
516 {
517 TransformationMatrix ctm;
518
519 // Rotate character around angle, and possibly scale.
520 ctm.translate(x, y);
521 ctm.rotate(angle);
522
523 if (pathData) {
524 ctm.scaleNonUniform(pathData->xScale, pathData->yScale);
525 ctm.translate(pathData->xShift, pathData->yShift);
526 ctm.rotate(pathData->orientationAngle);
527 }
528
529 ctm.translate(orientationShiftX - x, orientationShiftY - y);
530 return ctm;
531 }
532
533 } // namespace WebCore
534
535 #endif // ENABLE(SVG)
536