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 #if ENABLE(SVG)
23 #include "SVGTextLayoutEngineBaseline.h"
24
25 #include "Font.h"
26 #include "RenderObject.h"
27 #include "SVGRenderStyle.h"
28 #include "SVGTextMetrics.h"
29 #include "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 * lengthContext) const38 float SVGTextLayoutEngineBaseline::calculateBaselineShift(const SVGRenderStyle* style, SVGElement* lengthContext) const
39 {
40 if (style->baselineShift() == BS_LENGTH) {
41 SVGLength baselineShiftValueLength = style->baselineShiftValue();
42 if (baselineShiftValueLength.unitType() == LengthTypePercentage)
43 return baselineShiftValueLength.valueAsPercentage() * m_font.pixelSize();
44
45 return baselineShiftValueLength.value(lengthContext);
46 }
47
48 switch (style->baselineShift()) {
49 case BS_BASELINE:
50 return 0;
51 case BS_SUB:
52 return -m_font.fontMetrics().floatHeight() / 2;
53 case BS_SUPER:
54 return m_font.fontMetrics().floatHeight() / 2;
55 default:
56 ASSERT_NOT_REACHED();
57 return 0;
58 }
59 }
60
dominantBaselineToAlignmentBaseline(bool isVerticalText,const RenderObject * textRenderer) const61 EAlignmentBaseline SVGTextLayoutEngineBaseline::dominantBaselineToAlignmentBaseline(bool isVerticalText, const RenderObject* textRenderer) const
62 {
63 ASSERT(textRenderer);
64 ASSERT(textRenderer->style());
65 ASSERT(textRenderer->parent());
66 ASSERT(textRenderer->parent()->style());
67
68 const SVGRenderStyle* style = textRenderer->style()->svgStyle();
69 ASSERT(style);
70
71 EDominantBaseline baseline = style->dominantBaseline();
72 if (baseline == DB_AUTO) {
73 if (isVerticalText)
74 baseline = DB_CENTRAL;
75 else
76 baseline = DB_ALPHABETIC;
77 }
78
79 switch (baseline) {
80 case DB_USE_SCRIPT:
81 // FIXME: The dominant-baseline and the baseline-table components are set by determining the predominant script of the character data content.
82 return AB_ALPHABETIC;
83 case DB_NO_CHANGE:
84 return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent());
85 case DB_RESET_SIZE:
86 return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent());
87 case DB_IDEOGRAPHIC:
88 return AB_IDEOGRAPHIC;
89 case DB_ALPHABETIC:
90 return AB_ALPHABETIC;
91 case DB_HANGING:
92 return AB_HANGING;
93 case DB_MATHEMATICAL:
94 return AB_MATHEMATICAL;
95 case DB_CENTRAL:
96 return AB_CENTRAL;
97 case DB_MIDDLE:
98 return AB_MIDDLE;
99 case DB_TEXT_AFTER_EDGE:
100 return AB_TEXT_AFTER_EDGE;
101 case DB_TEXT_BEFORE_EDGE:
102 return AB_TEXT_BEFORE_EDGE;
103 default:
104 ASSERT_NOT_REACHED();
105 return AB_AUTO;
106 }
107 }
108
calculateAlignmentBaselineShift(bool isVerticalText,const RenderObject * textRenderer) const109 float SVGTextLayoutEngineBaseline::calculateAlignmentBaselineShift(bool isVerticalText, const RenderObject* textRenderer) const
110 {
111 ASSERT(textRenderer);
112 ASSERT(textRenderer->style());
113 ASSERT(textRenderer->style()->svgStyle());
114 ASSERT(textRenderer->parent());
115
116 const RenderObject* textRendererParent = textRenderer->parent();
117 ASSERT(textRendererParent);
118
119 EAlignmentBaseline baseline = textRenderer->style()->svgStyle()->alignmentBaseline();
120 if (baseline == AB_AUTO) {
121 baseline = dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent);
122 ASSERT(baseline != AB_AUTO);
123 }
124
125 const FontMetrics& fontMetrics = m_font.fontMetrics();
126
127 // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
128 switch (baseline) {
129 case AB_BASELINE:
130 return dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent);
131 case AB_BEFORE_EDGE:
132 case AB_TEXT_BEFORE_EDGE:
133 return fontMetrics.floatAscent();
134 case AB_MIDDLE:
135 return fontMetrics.xHeight() / 2;
136 case AB_CENTRAL:
137 return (fontMetrics.floatAscent() - fontMetrics.floatDescent()) / 2;
138 case AB_AFTER_EDGE:
139 case AB_TEXT_AFTER_EDGE:
140 case AB_IDEOGRAPHIC:
141 return fontMetrics.floatDescent();
142 case AB_ALPHABETIC:
143 return 0;
144 case AB_HANGING:
145 return fontMetrics.floatAscent() * 8 / 10.f;
146 case AB_MATHEMATICAL:
147 return fontMetrics.floatAscent() / 2;
148 default:
149 ASSERT_NOT_REACHED();
150 return 0;
151 }
152 }
153
calculateGlyphOrientationAngle(bool isVerticalText,const SVGRenderStyle * style,const UChar & character) const154 float SVGTextLayoutEngineBaseline::calculateGlyphOrientationAngle(bool isVerticalText, const SVGRenderStyle* style, const UChar& character) const
155 {
156 ASSERT(style);
157
158 switch (isVerticalText ? style->glyphOrientationVertical() : style->glyphOrientationHorizontal()) {
159 case GO_AUTO: {
160 // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees.
161 // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees.
162 unsigned int unicodeRange = findCharUnicodeRange(character);
163 if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic)
164 return 90;
165
166 return 0;
167 }
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 default:
176 return 0;
177 }
178 }
179
glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)180 static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)
181 {
182 return !fabsf(fmodf(orientationAngle, 180));
183 }
184
calculateGlyphAdvanceAndOrientation(bool isVerticalText,SVGTextMetrics & metrics,float angle,float & xOrientationShift,float & yOrientationShift) const185 float SVGTextLayoutEngineBaseline::calculateGlyphAdvanceAndOrientation(bool isVerticalText, SVGTextMetrics& metrics, float angle, float& xOrientationShift, float& yOrientationShift) const
186 {
187 bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(angle);
188
189 // The function is based on spec requirements:
190 //
191 // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of
192 // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph.
193 //
194 // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of
195 // 180 degrees, then the current text position is incremented according to the horizontal metrics of the glyph.
196
197 const FontMetrics& fontMetrics = m_font.fontMetrics();
198
199 // Vertical orientation handling.
200 if (isVerticalText) {
201 float ascentMinusDescent = fontMetrics.floatAscent() - fontMetrics.floatDescent();
202 if (!angle) {
203 xOrientationShift = (ascentMinusDescent - metrics.width()) / 2;
204 yOrientationShift = fontMetrics.floatAscent();
205 } else if (angle == 180)
206 xOrientationShift = (ascentMinusDescent + metrics.width()) / 2;
207 else if (angle == 270) {
208 yOrientationShift = metrics.width();
209 xOrientationShift = ascentMinusDescent;
210 }
211
212 // Vertical advance calculation.
213 if (angle && !orientationIsMultiplyOf180Degrees)
214 return metrics.width();
215
216 return metrics.height();
217 }
218
219 // Horizontal orientation handling.
220 if (angle == 90)
221 yOrientationShift = -metrics.width();
222 else if (angle == 180) {
223 xOrientationShift = metrics.width();
224 yOrientationShift = -fontMetrics.floatAscent();
225 } else if (angle == 270)
226 xOrientationShift = metrics.width();
227
228 // Horizontal advance calculation.
229 if (angle && !orientationIsMultiplyOf180Degrees)
230 return metrics.height();
231
232 return metrics.width();
233 }
234
235 }
236
237 #endif // ENABLE(SVG)
238