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 #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         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     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 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