Home | History | Annotate | Download | only in svg
      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