41cb7088d0e9b7dd05bff7e4316104e1d7060b88
[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* contextElement) 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(contextElement);
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     default:
55         ASSERT_NOT_REACHED();
56         return 0;
57     }
58 }
59
60 EAlignmentBaseline SVGTextLayoutEngineBaseline::dominantBaselineToAlignmentBaseline(bool isVerticalText, const RenderObject* textRenderer) const
61 {
62     ASSERT(textRenderer);
63     ASSERT(textRenderer->parent());
64
65     const SVGRenderStyle& svgStyle = textRenderer->style().svgStyle();
66
67     EDominantBaseline baseline = svgStyle.dominantBaseline();
68     if (baseline == DB_AUTO) {
69         if (isVerticalText)
70             baseline = DB_CENTRAL;
71         else
72             baseline = DB_ALPHABETIC;
73     }
74
75     switch (baseline) {
76     case DB_USE_SCRIPT:
77         // FIXME: The dominant-baseline and the baseline-table components are set by determining the predominant script of the character data content.
78         return AB_ALPHABETIC;
79     case DB_NO_CHANGE:
80         return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent());
81     case DB_RESET_SIZE:
82         return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent());
83     case DB_IDEOGRAPHIC:
84         return AB_IDEOGRAPHIC;
85     case DB_ALPHABETIC:
86         return AB_ALPHABETIC;
87     case DB_HANGING:
88         return AB_HANGING;
89     case DB_MATHEMATICAL:
90         return AB_MATHEMATICAL;
91     case DB_CENTRAL:
92         return AB_CENTRAL;
93     case DB_MIDDLE:
94         return AB_MIDDLE;
95     case DB_TEXT_AFTER_EDGE:
96         return AB_TEXT_AFTER_EDGE;
97     case DB_TEXT_BEFORE_EDGE:
98         return AB_TEXT_BEFORE_EDGE;
99     default:
100         ASSERT_NOT_REACHED();
101         return AB_AUTO;
102     }
103 }
104
105 float SVGTextLayoutEngineBaseline::calculateAlignmentBaselineShift(bool isVerticalText, const RenderObject* textRenderer) const
106 {
107     ASSERT(textRenderer);
108     ASSERT(textRenderer->parent());
109
110     const RenderObject* textRendererParent = textRenderer->parent();
111     ASSERT(textRendererParent);
112
113     EAlignmentBaseline baseline = textRenderer->style().svgStyle().alignmentBaseline();
114     if (baseline == AB_AUTO) {
115         baseline = dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent);
116         ASSERT(baseline != AB_AUTO);
117     }
118
119     const FontMetrics& fontMetrics = m_font.fontMetrics();
120
121     // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
122     switch (baseline) {
123     case AB_BASELINE:
124         return dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent);
125     case AB_BEFORE_EDGE:
126     case AB_TEXT_BEFORE_EDGE:
127         return fontMetrics.floatAscent();
128     case AB_MIDDLE:
129         return fontMetrics.xHeight() / 2;
130     case AB_CENTRAL:
131         return (fontMetrics.floatAscent() - fontMetrics.floatDescent()) / 2;
132     case AB_AFTER_EDGE:
133     case AB_TEXT_AFTER_EDGE:
134     case AB_IDEOGRAPHIC:
135         return fontMetrics.floatDescent();
136     case AB_ALPHABETIC:
137         return 0;
138     case AB_HANGING:
139         return fontMetrics.floatAscent() * 8 / 10.f;
140     case AB_MATHEMATICAL:
141         return fontMetrics.floatAscent() / 2;
142     default:
143         ASSERT_NOT_REACHED();
144         return 0;
145     }
146 }
147
148 float SVGTextLayoutEngineBaseline::calculateGlyphOrientationAngle(bool isVerticalText, const SVGRenderStyle* style, const UChar& character) const
149 {
150     ASSERT(style);
151
152     switch (isVerticalText ? style->glyphOrientationVertical() : style->glyphOrientationHorizontal()) {
153     case GO_AUTO:
154         // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees.
155         // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees.
156         // FIXME: There's not an accurate way to tell if text is fullwidth by looking at a single character.
157         switch (static_cast<UEastAsianWidth>(u_getIntPropertyValue(character, UCHAR_EAST_ASIAN_WIDTH))) {
158         case U_EA_NEUTRAL:
159         case U_EA_HALFWIDTH:
160         case U_EA_NARROW:
161             return 90;
162         case U_EA_AMBIGUOUS:
163         case U_EA_FULLWIDTH:
164         case U_EA_WIDE:
165             return 0;
166         case U_EA_COUNT:
167             ASSERT_NOT_REACHED();
168             break;
169         }
170         ASSERT_NOT_REACHED();
171         break;
172     case GO_90DEG:
173         return 90;
174     case GO_180DEG:
175         return 180;
176     case GO_270DEG:
177         return 270;
178     case GO_0DEG:
179         return 0;
180     }
181     ASSERT_NOT_REACHED();
182     return 0;
183 }
184
185 static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)
186 {
187     return !fabsf(fmodf(orientationAngle, 180));
188 }
189
190 float SVGTextLayoutEngineBaseline::calculateGlyphAdvanceAndOrientation(bool isVerticalText, SVGTextMetrics& metrics, float angle, float& xOrientationShift, float& yOrientationShift) const
191 {
192     bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(angle);
193
194     // The function is based on spec requirements:
195     //
196     // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of
197     // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph.
198     //
199     // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of
200     // 180 degrees, then the current text position is incremented according to the horizontal metrics of the glyph.
201
202     const FontMetrics& fontMetrics = m_font.fontMetrics();
203
204     // Vertical orientation handling.
205     if (isVerticalText) {
206         float ascentMinusDescent = fontMetrics.floatAscent() - fontMetrics.floatDescent();
207         if (!angle) {
208             xOrientationShift = (ascentMinusDescent - metrics.width()) / 2;
209             yOrientationShift = fontMetrics.floatAscent();
210         } else if (angle == 180)
211             xOrientationShift = (ascentMinusDescent + metrics.width()) / 2;
212         else if (angle == 270) {
213             yOrientationShift = metrics.width();
214             xOrientationShift = ascentMinusDescent;
215         }
216
217         // Vertical advance calculation.
218         if (angle && !orientationIsMultiplyOf180Degrees)
219             return metrics.width();
220
221         return metrics.height();
222     }
223
224     // Horizontal orientation handling.
225     if (angle == 90)
226         yOrientationShift = -metrics.width();
227     else if (angle == 180) {
228         xOrientationShift = metrics.width();
229         yOrientationShift = -fontMetrics.floatAscent();
230     } else if (angle == 270)
231         xOrientationShift = metrics.width();
232
233     // Horizontal advance calculation.
234     if (angle && !orientationIsMultiplyOf180Degrees)
235         return metrics.height();
236
237     return metrics.width();
238 }
239
240 }