Home | History | Annotate | Download | only in rendering
      1 /*
      2  * This file is part of the WebKit project.
      3  *
      4  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Library General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Library General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Library General Public License
     17  * along with this library; see the file COPYING.LIB.  If not, write to
     18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19  * Boston, MA 02110-1301, USA.
     20  *
     21  */
     22 
     23 #include "config.h"
     24 
     25 #if ENABLE(SVG)
     26 #include "SVGCharacterLayoutInfo.h"
     27 
     28 #include "InlineFlowBox.h"
     29 #include "InlineTextBox.h"
     30 #include "SVGLengthList.h"
     31 #include "SVGNumberList.h"
     32 #include "SVGTextPositioningElement.h"
     33 #include "RenderSVGTextPath.h"
     34 
     35 #include <float.h>
     36 
     37 namespace WebCore {
     38 
     39 // Helper function
     40 static float calculateBaselineShift(RenderObject* item)
     41 {
     42     const Font& font = item->style()->font();
     43     const SVGRenderStyle* svgStyle = item->style()->svgStyle();
     44 
     45     float baselineShift = 0.0f;
     46     if (svgStyle->baselineShift() == BS_LENGTH) {
     47         CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->baselineShiftValue());
     48         baselineShift = primitive->getFloatValue();
     49 
     50         if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
     51             baselineShift = baselineShift / 100.0f * font.pixelSize();
     52     } else {
     53         float baselineAscent = font.ascent() + font.descent();
     54 
     55         switch (svgStyle->baselineShift()) {
     56         case BS_BASELINE:
     57             break;
     58         case BS_SUB:
     59             baselineShift = -baselineAscent / 2.0f;
     60             break;
     61         case BS_SUPER:
     62             baselineShift = baselineAscent / 2.0f;
     63             break;
     64         default:
     65             ASSERT_NOT_REACHED();
     66         }
     67     }
     68 
     69     return baselineShift;
     70 }
     71 
     72 SVGCharacterLayoutInfo::SVGCharacterLayoutInfo(Vector<SVGChar>& chars)
     73     : curx(0.0f)
     74     , cury(0.0f)
     75     , angle(0.0f)
     76     , dx(0.0f)
     77     , dy(0.0f)
     78     , shiftx(0.0f)
     79     , shifty(0.0f)
     80     , pathExtraAdvance(0.0f)
     81     , pathTextLength(0.0f)
     82     , pathChunkLength(0.0f)
     83     , svgChars(chars)
     84     , nextDrawnSeperated(false)
     85     , xStackChanged(false)
     86     , yStackChanged(false)
     87     , dxStackChanged(false)
     88     , dyStackChanged(false)
     89     , angleStackChanged(false)
     90     , baselineShiftStackChanged(false)
     91     , pathLayout(false)
     92     , currentOffset(0.0f)
     93     , startOffset(0.0f)
     94     , layoutPathLength(0.0f)
     95 {
     96 }
     97 
     98 bool SVGCharacterLayoutInfo::xValueAvailable() const
     99 {
    100     return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size();
    101 }
    102 
    103 bool SVGCharacterLayoutInfo::yValueAvailable() const
    104 {
    105     return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size();
    106 }
    107 
    108 bool SVGCharacterLayoutInfo::dxValueAvailable() const
    109 {
    110     return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size();
    111 }
    112 
    113 bool SVGCharacterLayoutInfo::dyValueAvailable() const
    114 {
    115     return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size();
    116 }
    117 
    118 bool SVGCharacterLayoutInfo::angleValueAvailable() const
    119 {
    120     return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size();
    121 }
    122 
    123 bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const
    124 {
    125     return !baselineShiftStack.isEmpty();
    126 }
    127 
    128 float SVGCharacterLayoutInfo::xValueNext() const
    129 {
    130     ASSERT(!xStack.isEmpty());
    131     return xStack.last().valueAtCurrentPosition();
    132 }
    133 
    134 float SVGCharacterLayoutInfo::yValueNext() const
    135 {
    136     ASSERT(!yStack.isEmpty());
    137     return yStack.last().valueAtCurrentPosition();
    138 }
    139 
    140 float SVGCharacterLayoutInfo::dxValueNext() const
    141 {
    142     ASSERT(!dxStack.isEmpty());
    143     return dxStack.last().valueAtCurrentPosition();
    144 }
    145 
    146 float SVGCharacterLayoutInfo::dyValueNext() const
    147 {
    148     ASSERT(!dyStack.isEmpty());
    149     return dyStack.last().valueAtCurrentPosition();
    150 }
    151 
    152 float SVGCharacterLayoutInfo::angleValueNext() const
    153 {
    154     ASSERT(!angleStack.isEmpty());
    155     return angleStack.last().valueAtCurrentPosition();
    156 }
    157 
    158 float SVGCharacterLayoutInfo::baselineShiftValueNext() const
    159 {
    160     ASSERT(!baselineShiftStack.isEmpty());
    161     return baselineShiftStack.last();
    162 }
    163 
    164 void SVGCharacterLayoutInfo::processedSingleCharacter()
    165 {
    166     xStackWalk();
    167     yStackWalk();
    168     dxStackWalk();
    169     dyStackWalk();
    170     angleStackWalk();
    171     baselineShiftStackWalk();
    172 }
    173 
    174 void SVGCharacterLayoutInfo::processedChunk(float savedShiftX, float savedShiftY)
    175 {
    176     // baseline-shift doesn't span across ancestors, unlike dx/dy.
    177     curx += savedShiftX - shiftx;
    178     cury += savedShiftY - shifty;
    179 
    180     if (inPathLayout()) {
    181         shiftx = savedShiftX;
    182         shifty = savedShiftY;
    183     }
    184 
    185     // rotation also doesn't span
    186     angle = 0.0f;
    187 
    188     if (xStackChanged) {
    189         ASSERT(!xStack.isEmpty());
    190         xStack.removeLast();
    191         xStackChanged = false;
    192     }
    193 
    194     if (yStackChanged) {
    195         ASSERT(!yStack.isEmpty());
    196         yStack.removeLast();
    197         yStackChanged = false;
    198     }
    199 
    200     if (dxStackChanged) {
    201         ASSERT(!dxStack.isEmpty());
    202         dxStack.removeLast();
    203         dxStackChanged = false;
    204     }
    205 
    206     if (dyStackChanged) {
    207         ASSERT(!dyStack.isEmpty());
    208         dyStack.removeLast();
    209         dyStackChanged = false;
    210     }
    211 
    212     if (angleStackChanged) {
    213         ASSERT(!angleStack.isEmpty());
    214         angleStack.removeLast();
    215         angleStackChanged = false;
    216     }
    217 
    218     if (baselineShiftStackChanged) {
    219         ASSERT(!baselineShiftStack.isEmpty());
    220         baselineShiftStack.removeLast();
    221         baselineShiftStackChanged = false;
    222     }
    223 }
    224 
    225 bool SVGCharacterLayoutInfo::nextPathLayoutPointAndAngle(float glyphAdvance, float extraAdvance, float newOffset)
    226 {
    227     if (layoutPathLength <= 0.0f)
    228         return false;
    229 
    230     if (newOffset != FLT_MIN)
    231         currentOffset = startOffset + newOffset;
    232 
    233     // Respect translation along path (extraAdvance is orthogonal to the path)
    234     currentOffset += extraAdvance;
    235 
    236     float offset = currentOffset + glyphAdvance / 2.0f;
    237     currentOffset += glyphAdvance + pathExtraAdvance;
    238 
    239     if (offset < 0.0f || offset > layoutPathLength)
    240         return false;
    241 
    242     bool ok = false;
    243     FloatPoint point = layoutPath.pointAtLength(offset, ok);
    244     ASSERT(ok);
    245 
    246     curx = point.x();
    247     cury = point.y();
    248 
    249     angle = layoutPath.normalAngleAtLength(offset, ok);
    250     ASSERT(ok);
    251 
    252     // fprintf(stderr, "t: %f, x: %f, y: %f, angle: %f, glyphAdvance: %f\n", currentOffset, x, y, angle, glyphAdvance);
    253     return true;
    254 }
    255 
    256 bool SVGCharacterLayoutInfo::inPathLayout() const
    257 {
    258     return pathLayout;
    259 }
    260 
    261 void SVGCharacterLayoutInfo::setInPathLayout(bool value)
    262 {
    263     pathLayout = value;
    264 
    265     pathExtraAdvance = 0.0f;
    266     pathTextLength = 0.0f;
    267     pathChunkLength = 0.0f;
    268 }
    269 
    270 void SVGCharacterLayoutInfo::addLayoutInformation(InlineFlowBox* flowBox, float textAnchorStartOffset)
    271 {
    272     bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() &&
    273                            dxStack.isEmpty() && dyStack.isEmpty() &&
    274                            angleStack.isEmpty() && baselineShiftStack.isEmpty() &&
    275                            curx == 0.0f && cury == 0.0f;
    276 
    277     RenderSVGTextPath* textPath = toRenderSVGTextPath(flowBox->renderer());
    278     Path path = textPath->layoutPath();
    279 
    280     float baselineShift = calculateBaselineShift(textPath);
    281 
    282     layoutPath = path;
    283     layoutPathLength = path.length();
    284 
    285     if (layoutPathLength <= 0.0f)
    286         return;
    287 
    288     startOffset = textPath->startOffset();
    289 
    290     if (textPath->startOffset() >= 0.0f && textPath->startOffset() <= 1.0f)
    291         startOffset *= layoutPathLength;
    292 
    293     startOffset += textAnchorStartOffset;
    294     currentOffset = startOffset;
    295 
    296     // Only baseline-shift is handled through the normal layout system
    297     addStackContent(BaselineShiftStack, baselineShift);
    298 
    299     if (isInitialLayout) {
    300         xStackChanged = false;
    301         yStackChanged = false;
    302         dxStackChanged = false;
    303         dyStackChanged = false;
    304         angleStackChanged = false;
    305         baselineShiftStackChanged = false;
    306     }
    307 }
    308 
    309 void SVGCharacterLayoutInfo::addLayoutInformation(SVGTextPositioningElement* element)
    310 {
    311     bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() &&
    312                            dxStack.isEmpty() && dyStack.isEmpty() &&
    313                            angleStack.isEmpty() && baselineShiftStack.isEmpty() &&
    314                            curx == 0.0f && cury == 0.0f;
    315 
    316     float baselineShift = calculateBaselineShift(element->renderer());
    317 
    318     addStackContent(XStack, element->x(), element);
    319     addStackContent(YStack, element->y(), element);
    320     addStackContent(DxStack, element->dx(), element);
    321     addStackContent(DyStack, element->dy(), element);
    322     addStackContent(AngleStack, element->rotate());
    323     addStackContent(BaselineShiftStack, baselineShift);
    324 
    325     if (isInitialLayout) {
    326         xStackChanged = false;
    327         yStackChanged = false;
    328         dxStackChanged = false;
    329         dyStackChanged = false;
    330         angleStackChanged = false;
    331         baselineShiftStackChanged = false;
    332     }
    333 }
    334 
    335 void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGNumberList* list)
    336 {
    337     unsigned length = list->numberOfItems();
    338     if (!length)
    339         return;
    340 
    341     PositionedFloatVector newLayoutInfo;
    342 
    343     // TODO: Convert more efficiently!
    344     ExceptionCode ec = 0;
    345     for (unsigned i = 0; i < length; ++i) {
    346         float value = list->getItem(i, ec);
    347         ASSERT(ec == 0);
    348 
    349         newLayoutInfo.append(value);
    350     }
    351 
    352     addStackContent(type, newLayoutInfo);
    353 }
    354 
    355 void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGLengthList* list, const SVGElement* context)
    356 {
    357     unsigned length = list->numberOfItems();
    358     if (!length)
    359         return;
    360 
    361     PositionedFloatVector newLayoutInfo;
    362 
    363     ExceptionCode ec = 0;
    364     for (unsigned i = 0; i < length; ++i) {
    365         float value = list->getItem(i, ec).value(context);
    366         ASSERT(ec == 0);
    367 
    368         newLayoutInfo.append(value);
    369     }
    370 
    371     addStackContent(type, newLayoutInfo);
    372 }
    373 
    374 void SVGCharacterLayoutInfo::addStackContent(StackType type, const PositionedFloatVector& list)
    375 {
    376     switch (type) {
    377     case XStack:
    378         xStackChanged = true;
    379         xStack.append(list);
    380         break;
    381     case YStack:
    382         yStackChanged = true;
    383         yStack.append(list);
    384         break;
    385     case DxStack:
    386         dxStackChanged = true;
    387         dxStack.append(list);
    388         break;
    389     case DyStack:
    390         dyStackChanged = true;
    391         dyStack.append(list);
    392         break;
    393     case AngleStack:
    394         angleStackChanged = true;
    395         angleStack.append(list);
    396         break;
    397     default:
    398         ASSERT_NOT_REACHED();
    399     }
    400 }
    401 
    402 void SVGCharacterLayoutInfo::addStackContent(StackType type, float value)
    403 {
    404     if (value == 0.0f)
    405         return;
    406 
    407     switch (type) {
    408     case BaselineShiftStack:
    409         baselineShiftStackChanged = true;
    410         baselineShiftStack.append(value);
    411         break;
    412     default:
    413         ASSERT_NOT_REACHED();
    414     }
    415 }
    416 
    417 void SVGCharacterLayoutInfo::xStackWalk()
    418 {
    419     unsigned i = 1;
    420 
    421     while (!xStack.isEmpty()) {
    422         PositionedFloatVector& cur = xStack.last();
    423         if (i + cur.position() < cur.size()) {
    424             cur.advance(i);
    425             break;
    426         }
    427 
    428         i += cur.position();
    429         xStack.removeLast();
    430         xStackChanged = false;
    431     }
    432 }
    433 
    434 void SVGCharacterLayoutInfo::yStackWalk()
    435 {
    436     unsigned i = 1;
    437 
    438     while (!yStack.isEmpty()) {
    439         PositionedFloatVector& cur = yStack.last();
    440         if (i + cur.position() < cur.size()) {
    441             cur.advance(i);
    442             break;
    443         }
    444 
    445         i += cur.position();
    446         yStack.removeLast();
    447         yStackChanged = false;
    448     }
    449 }
    450 
    451 void SVGCharacterLayoutInfo::dxStackWalk()
    452 {
    453     unsigned i = 1;
    454 
    455     while (!dxStack.isEmpty()) {
    456         PositionedFloatVector& cur = dxStack.last();
    457         if (i + cur.position() < cur.size()) {
    458             cur.advance(i);
    459             break;
    460         }
    461 
    462         i += cur.position();
    463         dxStack.removeLast();
    464         dxStackChanged = false;
    465     }
    466 }
    467 
    468 void SVGCharacterLayoutInfo::dyStackWalk()
    469 {
    470     unsigned i = 1;
    471 
    472     while (!dyStack.isEmpty()) {
    473         PositionedFloatVector& cur = dyStack.last();
    474         if (i + cur.position() < cur.size()) {
    475             cur.advance(i);
    476             break;
    477         }
    478 
    479         i += cur.position();
    480         dyStack.removeLast();
    481         dyStackChanged = false;
    482     }
    483 }
    484 
    485 void SVGCharacterLayoutInfo::angleStackWalk()
    486 {
    487     unsigned i = 1;
    488 
    489     while (!angleStack.isEmpty()) {
    490         PositionedFloatVector& cur = angleStack.last();
    491         if (i + cur.position() < cur.size()) {
    492             cur.advance(i);
    493             break;
    494         }
    495 
    496         i += cur.position();
    497         angleStack.removeLast();
    498         angleStackChanged = false;
    499     }
    500 }
    501 
    502 void SVGCharacterLayoutInfo::baselineShiftStackWalk()
    503 {
    504     if (!baselineShiftStack.isEmpty()) {
    505         baselineShiftStack.removeLast();
    506         baselineShiftStackChanged = false;
    507     }
    508 }
    509 
    510 bool SVGChar::isHidden() const
    511 {
    512     return pathData && pathData->hidden;
    513 }
    514 
    515 AffineTransform SVGChar::characterTransform() const
    516 {
    517     AffineTransform ctm;
    518 
    519     // Rotate character around angle, and possibly scale.
    520     ctm.translate(x, y);
    521     ctm.rotate(angle);
    522 
    523     if (pathData) {
    524         ctm.scaleNonUniform(pathData->xScale, pathData->yScale);
    525         ctm.translate(pathData->xShift, pathData->yShift);
    526         ctm.rotate(pathData->orientationAngle);
    527     }
    528 
    529     ctm.translate(orientationShiftX - x, orientationShiftY - y);
    530     return ctm;
    531 }
    532 
    533 } // namespace WebCore
    534 
    535 #endif // ENABLE(SVG)
    536