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 33 SVGTextLayoutEngineBaseline::SVGTextLayoutEngineBaseline(const Font& font) 34 : m_font(font) 35 { 36 } 37 38 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 61 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 109 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 154 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 180 static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle) 181 { 182 return !fabsf(fmodf(orientationAngle, 180)); 183 } 184 185 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