Clean up: Use references instead of pointers in more SVG files
[WebKit-https.git] / Source / WebCore / rendering / svg / SVGTextLayoutEngineBaseline.cpp
1 /*
2  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "SVGTextLayoutEngineBaseline.h"
22
23 #include "FontCascade.h"
24 #include "RenderElement.h"
25 #include "SVGLengthContext.h"
26 #include "SVGRenderStyle.h"
27 #include "SVGTextMetrics.h"
28
29 namespace WebCore {
30
31 SVGTextLayoutEngineBaseline::SVGTextLayoutEngineBaseline(const FontCascade& font)
32     : m_font(font)
33 {
34 }
35
36 float SVGTextLayoutEngineBaseline::calculateBaselineShift(const SVGRenderStyle& style, SVGElement* context) const
37 {
38     if (style.baselineShift() == BS_LENGTH) {
39         SVGLength baselineShiftValueLength = style.baselineShiftValue();
40         if (baselineShiftValueLength.unitType() == LengthTypePercentage)
41             return baselineShiftValueLength.valueAsPercentage() * m_font.pixelSize();
42
43         SVGLengthContext lengthContext(context);
44         return baselineShiftValueLength.value(lengthContext);
45     }
46
47     switch (style.baselineShift()) {
48     case BS_BASELINE:
49         return 0;
50     case BS_SUB:
51         return -m_font.fontMetrics().floatHeight() / 2;
52     case BS_SUPER:
53         return m_font.fontMetrics().floatHeight() / 2;
54     case BS_LENGTH:
55         break;
56     }
57     ASSERT_NOT_REACHED();
58     return 0;
59 }
60
61 EAlignmentBaseline SVGTextLayoutEngineBaseline::dominantBaselineToAlignmentBaseline(bool isVerticalText, const RenderObject* textRenderer) const
62 {
63     ASSERT(textRenderer);
64     ASSERT(textRenderer->parent());
65
66     const SVGRenderStyle& svgStyle = textRenderer->style().svgStyle();
67
68     EDominantBaseline baseline = svgStyle.dominantBaseline();
69     if (baseline == DB_AUTO) {
70         if (isVerticalText)
71             baseline = DB_CENTRAL;
72         else
73             baseline = DB_ALPHABETIC;
74     }
75
76     switch (baseline) {
77     case DB_USE_SCRIPT:
78         // FIXME: The dominant-baseline and the baseline-table components are set by determining the predominant script of the character data content.
79         return AB_ALPHABETIC;
80     case DB_NO_CHANGE:
81         return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent());
82     case DB_RESET_SIZE:
83         return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent());
84     case DB_IDEOGRAPHIC:
85         return AB_IDEOGRAPHIC;
86     case DB_ALPHABETIC:
87         return AB_ALPHABETIC;
88     case DB_HANGING:
89         return AB_HANGING;
90     case DB_MATHEMATICAL:
91         return AB_MATHEMATICAL;
92     case DB_CENTRAL:
93         return AB_CENTRAL;
94     case DB_MIDDLE:
95         return AB_MIDDLE;
96     case DB_TEXT_AFTER_EDGE:
97         return AB_TEXT_AFTER_EDGE;
98     case DB_TEXT_BEFORE_EDGE:
99         return AB_TEXT_BEFORE_EDGE;
100     default:
101         ASSERT_NOT_REACHED();
102         return AB_AUTO;
103     }
104 }
105
106 float SVGTextLayoutEngineBaseline::calculateAlignmentBaselineShift(bool isVerticalText, const RenderObject& textRenderer) const
107 {
108     const RenderObject* textRendererParent = textRenderer.parent();
109     ASSERT(textRendererParent);
110
111     EAlignmentBaseline baseline = textRenderer.style().svgStyle().alignmentBaseline();
112     if (baseline == AB_AUTO) {
113         baseline = dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent);
114         ASSERT(baseline != AB_AUTO);
115     }
116
117     const FontMetrics& fontMetrics = m_font.fontMetrics();
118
119     // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
120     switch (baseline) {
121     case AB_BASELINE:
122         return dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent);
123     case AB_BEFORE_EDGE:
124     case AB_TEXT_BEFORE_EDGE:
125         return fontMetrics.floatAscent();
126     case AB_MIDDLE:
127         return fontMetrics.xHeight() / 2;
128     case AB_CENTRAL:
129         return (fontMetrics.floatAscent() - fontMetrics.floatDescent()) / 2;
130     case AB_AFTER_EDGE:
131     case AB_TEXT_AFTER_EDGE:
132     case AB_IDEOGRAPHIC:
133         return fontMetrics.floatDescent();
134     case AB_ALPHABETIC:
135         return 0;
136     case AB_HANGING:
137         return fontMetrics.floatAscent() * 8 / 10.f;
138     case AB_MATHEMATICAL:
139         return fontMetrics.floatAscent() / 2;
140     default:
141         ASSERT_NOT_REACHED();
142         return 0;
143     }
144 }
145
146 float SVGTextLayoutEngineBaseline::calculateGlyphOrientationAngle(bool isVerticalText, const SVGRenderStyle& style, const UChar& character) const
147 {
148     switch (isVerticalText ? style.glyphOrientationVertical() : style.glyphOrientationHorizontal()) {
149     case GO_AUTO:
150         // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees.
151         // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees.
152         // FIXME: There's not an accurate way to tell if text is fullwidth by looking at a single character.
153         switch (static_cast<UEastAsianWidth>(u_getIntPropertyValue(character, UCHAR_EAST_ASIAN_WIDTH))) {
154         case U_EA_NEUTRAL:
155         case U_EA_HALFWIDTH:
156         case U_EA_NARROW:
157             return 90;
158         case U_EA_AMBIGUOUS:
159         case U_EA_FULLWIDTH:
160         case U_EA_WIDE:
161             return 0;
162         case U_EA_COUNT:
163             ASSERT_NOT_REACHED();
164             break;
165         }
166         ASSERT_NOT_REACHED();
167         break;
168     case GO_90DEG:
169         return 90;
170     case GO_180DEG:
171         return 180;
172     case GO_270DEG:
173         return 270;
174     case GO_0DEG:
175         return 0;
176     }
177     ASSERT_NOT_REACHED();
178     return 0;
179 }
180
181 static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)
182 {
183     return !fabsf(fmodf(orientationAngle, 180));
184 }
185
186 float SVGTextLayoutEngineBaseline::calculateGlyphAdvanceAndOrientation(bool isVerticalText, SVGTextMetrics& metrics, float angle, float& xOrientationShift, float& yOrientationShift) const
187 {
188     bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(angle);
189
190     // The function is based on spec requirements:
191     //
192     // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of
193     // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph.
194     //
195     // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of
196     // 180 degrees, then the current text position is incremented according to the horizontal metrics of the glyph.
197
198     const FontMetrics& fontMetrics = m_font.fontMetrics();
199
200     // Vertical orientation handling.
201     if (isVerticalText) {
202         float ascentMinusDescent = fontMetrics.floatAscent() - fontMetrics.floatDescent();
203         if (!angle) {
204             xOrientationShift = (ascentMinusDescent - metrics.width()) / 2;
205             yOrientationShift = fontMetrics.floatAscent();
206         } else if (angle == 180)
207             xOrientationShift = (ascentMinusDescent + metrics.width()) / 2;
208         else if (angle == 270) {
209             yOrientationShift = metrics.width();
210             xOrientationShift = ascentMinusDescent;
211         }
212
213         // Vertical advance calculation.
214         if (angle && !orientationIsMultiplyOf180Degrees)
215             return metrics.width();
216
217         return metrics.height();
218     }
219
220     // Horizontal orientation handling.
221     if (angle == 90)
222         yOrientationShift = -metrics.width();
223     else if (angle == 180) {
224         xOrientationShift = metrics.width();
225         yOrientationShift = -fontMetrics.floatAscent();
226     } else if (angle == 270)
227         xOrientationShift = metrics.width();
228
229     // Horizontal advance calculation.
230     if (angle && !orientationIsMultiplyOf180Degrees)
231         return metrics.height();
232
233     return metrics.width();
234 }
235
236 }