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
22 #include "core/rendering/svg/SVGTextLayoutEngineBaseline.h"
23
24 #include "core/rendering/RenderObject.h"
25 #include "core/rendering/style/SVGRenderStyle.h"
26 #include "core/rendering/svg/SVGTextMetrics.h"
27 #include "core/svg/SVGLengthContext.h"
28 #include "platform/fonts/Font.h"
29 #include "platform/text/UnicodeRange.h"
30
31 namespace WebCore {
32
SVGTextLayoutEngineBaseline(const Font & font)33 SVGTextLayoutEngineBaseline::SVGTextLayoutEngineBaseline(const Font& font)
34 : m_font(font)
35 {
36 }
37
calculateBaselineShift(const SVGRenderStyle * style,SVGElement * contextElement) const38 float SVGTextLayoutEngineBaseline::calculateBaselineShift(const SVGRenderStyle* style, SVGElement* contextElement) const
39 {
40 if (style->baselineShift() == BS_LENGTH) {
41 RefPtr<SVGLength> baselineShiftValueLength = style->baselineShiftValue();
42 if (baselineShiftValueLength->unitType() == LengthTypePercentage)
43 return baselineShiftValueLength->valueAsPercentage() * m_font.fontDescription().computedPixelSize();
44
45 SVGLengthContext lengthContext(contextElement);
46 return baselineShiftValueLength->value(lengthContext);
47 }
48
49 switch (style->baselineShift()) {
50 case BS_BASELINE:
51 return 0;
52 case BS_SUB:
53 return -m_font.fontMetrics().floatHeight() / 2;
54 case BS_SUPER:
55 return m_font.fontMetrics().floatHeight() / 2;
56 default:
57 ASSERT_NOT_REACHED();
58 return 0;
59 }
60 }
61
dominantBaselineToAlignmentBaseline(bool isVerticalText,const RenderObject * textRenderer) const62 EAlignmentBaseline SVGTextLayoutEngineBaseline::dominantBaselineToAlignmentBaseline(bool isVerticalText, const RenderObject* textRenderer) const
63 {
64 ASSERT(textRenderer);
65 ASSERT(textRenderer->style());
66 ASSERT(textRenderer->parent());
67 ASSERT(textRenderer->parent()->style());
68
69 const SVGRenderStyle* style = textRenderer->style()->svgStyle();
70 ASSERT(style);
71
72 EDominantBaseline baseline = style->dominantBaseline();
73 if (baseline == DB_AUTO) {
74 if (isVerticalText)
75 baseline = DB_CENTRAL;
76 else
77 baseline = DB_ALPHABETIC;
78 }
79
80 switch (baseline) {
81 case DB_USE_SCRIPT:
82 // FIXME: The dominant-baseline and the baseline-table components are set by determining the predominant script of the character data content.
83 return AB_ALPHABETIC;
84 case DB_NO_CHANGE:
85 return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent());
86 case DB_RESET_SIZE:
87 return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent());
88 case DB_IDEOGRAPHIC:
89 return AB_IDEOGRAPHIC;
90 case DB_ALPHABETIC:
91 return AB_ALPHABETIC;
92 case DB_HANGING:
93 return AB_HANGING;
94 case DB_MATHEMATICAL:
95 return AB_MATHEMATICAL;
96 case DB_CENTRAL:
97 return AB_CENTRAL;
98 case DB_MIDDLE:
99 return AB_MIDDLE;
100 case DB_TEXT_AFTER_EDGE:
101 return AB_TEXT_AFTER_EDGE;
102 case DB_TEXT_BEFORE_EDGE:
103 return AB_TEXT_BEFORE_EDGE;
104 default:
105 ASSERT_NOT_REACHED();
106 return AB_AUTO;
107 }
108 }
109
calculateAlignmentBaselineShift(bool isVerticalText,const RenderObject * textRenderer) const110 float SVGTextLayoutEngineBaseline::calculateAlignmentBaselineShift(bool isVerticalText, const RenderObject* textRenderer) const
111 {
112 ASSERT(textRenderer);
113 ASSERT(textRenderer->style());
114 ASSERT(textRenderer->style()->svgStyle());
115 ASSERT(textRenderer->parent());
116
117 const RenderObject* textRendererParent = textRenderer->parent();
118 ASSERT(textRendererParent);
119
120 EAlignmentBaseline baseline = textRenderer->style()->svgStyle()->alignmentBaseline();
121 if (baseline == AB_AUTO) {
122 baseline = dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent);
123 ASSERT(baseline != AB_AUTO);
124 }
125
126 const FontMetrics& fontMetrics = m_font.fontMetrics();
127
128 // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
129 switch (baseline) {
130 case AB_BASELINE:
131 return dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent);
132 case AB_BEFORE_EDGE:
133 case AB_TEXT_BEFORE_EDGE:
134 return fontMetrics.floatAscent();
135 case AB_MIDDLE:
136 return fontMetrics.xHeight() / 2;
137 case AB_CENTRAL:
138 return (fontMetrics.floatAscent() - fontMetrics.floatDescent()) / 2;
139 case AB_AFTER_EDGE:
140 case AB_TEXT_AFTER_EDGE:
141 case AB_IDEOGRAPHIC:
142 return fontMetrics.floatDescent();
143 case AB_ALPHABETIC:
144 return 0;
145 case AB_HANGING:
146 return fontMetrics.floatAscent() * 8 / 10.f;
147 case AB_MATHEMATICAL:
148 return fontMetrics.floatAscent() / 2;
149 default:
150 ASSERT_NOT_REACHED();
151 return 0;
152 }
153 }
154
calculateGlyphOrientationAngle(bool isVerticalText,const SVGRenderStyle * style,const UChar & character) const155 float SVGTextLayoutEngineBaseline::calculateGlyphOrientationAngle(bool isVerticalText, const SVGRenderStyle* style, const UChar& character) const
156 {
157 ASSERT(style);
158
159 switch (isVerticalText ? style->glyphOrientationVertical() : style->glyphOrientationHorizontal()) {
160 case GO_AUTO: {
161 // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees.
162 // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees.
163 unsigned unicodeRange = findCharUnicodeRange(character);
164 if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic)
165 return 90;
166
167 return 0;
168 }
169 case GO_90DEG:
170 return 90;
171 case GO_180DEG:
172 return 180;
173 case GO_270DEG:
174 return 270;
175 case GO_0DEG:
176 default:
177 return 0;
178 }
179 }
180
glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)181 static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)
182 {
183 return !fabsf(fmodf(orientationAngle, 180));
184 }
185
calculateGlyphAdvanceAndOrientation(bool isVerticalText,SVGTextMetrics & metrics,float angle,float & xOrientationShift,float & yOrientationShift) const186 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 }
237