• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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