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 blink { 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 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 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 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->parent()); 114 115 const RenderObject* textRendererParent = textRenderer->parent(); 116 ASSERT(textRendererParent); 117 118 EAlignmentBaseline baseline = textRenderer->style()->svgStyle().alignmentBaseline(); 119 if (baseline == AB_AUTO || baseline == AB_BASELINE) { 120 baseline = dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent); 121 ASSERT(baseline != AB_AUTO && baseline != AB_BASELINE); 122 } 123 124 const FontMetrics& fontMetrics = m_font.fontMetrics(); 125 126 // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling 127 switch (baseline) { 128 case AB_BEFORE_EDGE: 129 case AB_TEXT_BEFORE_EDGE: 130 return fontMetrics.floatAscent(); 131 case AB_MIDDLE: 132 return fontMetrics.xHeight() / 2; 133 case AB_CENTRAL: 134 return (fontMetrics.floatAscent() - fontMetrics.floatDescent()) / 2; 135 case AB_AFTER_EDGE: 136 case AB_TEXT_AFTER_EDGE: 137 case AB_IDEOGRAPHIC: 138 return -fontMetrics.floatDescent(); 139 case AB_ALPHABETIC: 140 return 0; 141 case AB_HANGING: 142 return fontMetrics.floatAscent() * 8 / 10.f; 143 case AB_MATHEMATICAL: 144 return fontMetrics.floatAscent() / 2; 145 case AB_BASELINE: 146 default: 147 ASSERT_NOT_REACHED(); 148 return 0; 149 } 150 } 151 152 float SVGTextLayoutEngineBaseline::calculateGlyphOrientationAngle(bool isVerticalText, const SVGRenderStyle& style, const UChar& character) const 153 { 154 switch (isVerticalText ? style.glyphOrientationVertical() : style.glyphOrientationHorizontal()) { 155 case GO_AUTO: { 156 // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees. 157 // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees. 158 unsigned unicodeRange = findCharUnicodeRange(character); 159 if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic) 160 return 90; 161 162 return 0; 163 } 164 case GO_90DEG: 165 return 90; 166 case GO_180DEG: 167 return 180; 168 case GO_270DEG: 169 return 270; 170 case GO_0DEG: 171 default: 172 return 0; 173 } 174 } 175 176 static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle) 177 { 178 return !fabsf(fmodf(orientationAngle, 180)); 179 } 180 181 float SVGTextLayoutEngineBaseline::calculateGlyphAdvanceAndOrientation(bool isVerticalText, const SVGTextMetrics& metrics, float angle, float& xOrientationShift, float& yOrientationShift) const 182 { 183 bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(angle); 184 185 // The function is based on spec requirements: 186 // 187 // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of 188 // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph. 189 // 190 // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of 191 // 180 degrees, then the current text position is incremented according to the horizontal metrics of the glyph. 192 193 const FontMetrics& fontMetrics = m_font.fontMetrics(); 194 195 // Vertical orientation handling. 196 if (isVerticalText) { 197 float ascentMinusDescent = fontMetrics.floatAscent() - fontMetrics.floatDescent(); 198 if (!angle) { 199 xOrientationShift = (ascentMinusDescent - metrics.width()) / 2; 200 yOrientationShift = fontMetrics.floatAscent(); 201 } else if (angle == 180) 202 xOrientationShift = (ascentMinusDescent + metrics.width()) / 2; 203 else if (angle == 270) { 204 yOrientationShift = metrics.width(); 205 xOrientationShift = ascentMinusDescent; 206 } 207 208 // Vertical advance calculation. 209 if (angle && !orientationIsMultiplyOf180Degrees) 210 return metrics.width(); 211 212 return metrics.height(); 213 } 214 215 // Horizontal orientation handling. 216 if (angle == 90) 217 yOrientationShift = -metrics.width(); 218 else if (angle == 180) { 219 xOrientationShift = metrics.width(); 220 yOrientationShift = -fontMetrics.floatAscent(); 221 } else if (angle == 270) 222 xOrientationShift = metrics.width(); 223 224 // Horizontal advance calculation. 225 if (angle && !orientationIsMultiplyOf180Degrees) 226 return metrics.height(); 227 228 return metrics.width(); 229 } 230 231 } 232