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 33 SVGTextLayoutEngineBaseline::SVGTextLayoutEngineBaseline(const Font& font) 34 : m_font(font) 35 { 36 } 37 38 float SVGTextLayoutEngineBaseline::calculateBaselineShift(const SVGRenderStyle* style, SVGElement* contextElement) 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 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 62 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 110 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 155 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 int 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 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 } 237