Reviewed by Simon Fraser.
[WebKit-https.git] / WebCore / rendering / SVGCharacterLayoutInfo.cpp
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
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
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
98 bool SVGCharacterLayoutInfo::xValueAvailable() const
99 {
100     return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size();
101 }
102
103 bool SVGCharacterLayoutInfo::yValueAvailable() const
104 {
105     return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size();
106 }
107
108 bool SVGCharacterLayoutInfo::dxValueAvailable() const
109 {
110     return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size();
111 }
112
113 bool SVGCharacterLayoutInfo::dyValueAvailable() const
114 {
115     return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size();
116 }
117
118 bool SVGCharacterLayoutInfo::angleValueAvailable() const
119 {
120     return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size();
121 }
122
123 bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const
124 {
125     return !baselineShiftStack.isEmpty();
126 }
127
128 float SVGCharacterLayoutInfo::xValueNext() const
129 {
130     ASSERT(!xStack.isEmpty());
131     return xStack.last().valueAtCurrentPosition();
132 }
133
134 float SVGCharacterLayoutInfo::yValueNext() const
135 {
136     ASSERT(!yStack.isEmpty());
137     return yStack.last().valueAtCurrentPosition();
138 }
139
140 float SVGCharacterLayoutInfo::dxValueNext() const
141 {
142     ASSERT(!dxStack.isEmpty());
143     return dxStack.last().valueAtCurrentPosition();
144 }
145
146 float SVGCharacterLayoutInfo::dyValueNext() const
147 {
148     ASSERT(!dyStack.isEmpty());
149     return dyStack.last().valueAtCurrentPosition();
150 }
151
152 float SVGCharacterLayoutInfo::angleValueNext() const
153 {
154     ASSERT(!angleStack.isEmpty());
155     return angleStack.last().valueAtCurrentPosition();
156 }
157
158 float SVGCharacterLayoutInfo::baselineShiftValueNext() const
159 {
160     ASSERT(!baselineShiftStack.isEmpty());
161     return baselineShiftStack.last();
162 }
163
164 void SVGCharacterLayoutInfo::processedSingleCharacter()
165 {
166     xStackWalk();
167     yStackWalk();
168     dxStackWalk();
169     dyStackWalk();
170     angleStackWalk();
171     baselineShiftStackWalk();
172 }
173
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
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
256 bool SVGCharacterLayoutInfo::inPathLayout() const
257 {
258     return pathLayout;
259 }
260
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
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 = static_cast<RenderSVGTextPath*>(flowBox->object());
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
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
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
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
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
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
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
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
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
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
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
502 void SVGCharacterLayoutInfo::baselineShiftStackWalk()
503 {
504     if (!baselineShiftStack.isEmpty()) {
505         baselineShiftStack.removeLast();
506         baselineShiftStackChanged = false;
507     }
508 }
509
510 bool SVGChar::isHidden() const
511 {
512     return pathData && pathData->hidden;
513 }
514
515 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)