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
37namespace WebCore {
38
39// Helper function
40static 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
72SVGCharacterLayoutInfo::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
98bool SVGCharacterLayoutInfo::xValueAvailable() const
99{
100    return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size();
101}
102
103bool SVGCharacterLayoutInfo::yValueAvailable() const
104{
105    return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size();
106}
107
108bool SVGCharacterLayoutInfo::dxValueAvailable() const
109{
110    return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size();
111}
112
113bool SVGCharacterLayoutInfo::dyValueAvailable() const
114{
115    return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size();
116}
117
118bool SVGCharacterLayoutInfo::angleValueAvailable() const
119{
120    return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size();
121}
122
123bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const
124{
125    return !baselineShiftStack.isEmpty();
126}
127
128float SVGCharacterLayoutInfo::xValueNext() const
129{
130    ASSERT(!xStack.isEmpty());
131    return xStack.last().valueAtCurrentPosition();
132}
133
134float SVGCharacterLayoutInfo::yValueNext() const
135{
136    ASSERT(!yStack.isEmpty());
137    return yStack.last().valueAtCurrentPosition();
138}
139
140float SVGCharacterLayoutInfo::dxValueNext() const
141{
142    ASSERT(!dxStack.isEmpty());
143    return dxStack.last().valueAtCurrentPosition();
144}
145
146float SVGCharacterLayoutInfo::dyValueNext() const
147{
148    ASSERT(!dyStack.isEmpty());
149    return dyStack.last().valueAtCurrentPosition();
150}
151
152float SVGCharacterLayoutInfo::angleValueNext() const
153{
154    ASSERT(!angleStack.isEmpty());
155    return angleStack.last().valueAtCurrentPosition();
156}
157
158float SVGCharacterLayoutInfo::baselineShiftValueNext() const
159{
160    ASSERT(!baselineShiftStack.isEmpty());
161    return baselineShiftStack.last();
162}
163
164void SVGCharacterLayoutInfo::processedSingleCharacter()
165{
166    xStackWalk();
167    yStackWalk();
168    dxStackWalk();
169    dyStackWalk();
170    angleStackWalk();
171    baselineShiftStackWalk();
172}
173
174void 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
225bool 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
256bool SVGCharacterLayoutInfo::inPathLayout() const
257{
258    return pathLayout;
259}
260
261void SVGCharacterLayoutInfo::setInPathLayout(bool value)
262{
263    pathLayout = value;
264
265    pathExtraAdvance = 0.0f;
266    pathTextLength = 0.0f;
267    pathChunkLength = 0.0f;
268}
269
270void 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
309void 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
335void 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
355void 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
374void 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
402void 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
417void 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
434void 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
451void 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
468void 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
485void 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
502void SVGCharacterLayoutInfo::baselineShiftStackWalk()
503{
504    if (!baselineShiftStack.isEmpty()) {
505        baselineShiftStack.removeLast();
506        baselineShiftStackChanged = false;
507    }
508}
509
510bool SVGChar::isHidden() const
511{
512    return pathData && pathData->hidden;
513}
514
515AffineTransform SVGChar::characterTransform() const
516{
517    AffineTransform 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