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 "Font.h" 24#include "RenderElement.h" 25#include "SVGLengthContext.h" 26#include "SVGRenderStyle.h" 27#include "SVGTextMetrics.h" 28 29namespace WebCore { 30 31SVGTextLayoutEngineBaseline::SVGTextLayoutEngineBaseline(const Font& font) 32 : m_font(font) 33{ 34} 35 36float 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 60EAlignmentBaseline 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 105float 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 148float 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 185static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle) 186{ 187 return !fabsf(fmodf(orientationAngle, 180)); 188} 189 190float 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} 241