1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 2004-2005 Allan Sandfeld Jensen (kde (at) carewolf.com) 4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit (at) nickshanks.com) 5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. 6 * Copyright (C) 2007 Alexey Proskuryakov <ap (at) webkit.org> 7 * Copyright (C) 2007, 2008 Eric Seidel <eric (at) webkit.org> 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. 10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. 11 * 12 * This library is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU Library General Public 14 * License as published by the Free Software Foundation; either 15 * version 2 of the License, or (at your option) any later version. 16 * 17 * This library is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 * Library General Public License for more details. 21 * 22 * You should have received a copy of the GNU Library General Public License 23 * along with this library; see the file COPYING.LIB. If not, write to 24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 * Boston, MA 02110-1301, USA. 26 */ 27 28 #include "config.h" 29 #include "core/css/resolver/CSSToStyleMap.h" 30 31 #include "core/CSSValueKeywords.h" 32 #include "core/animation/css/CSSAnimationData.h" 33 #include "core/css/CSSBorderImageSliceValue.h" 34 #include "core/css/CSSPrimitiveValue.h" 35 #include "core/css/CSSPrimitiveValueMappings.h" 36 #include "core/css/CSSTimingFunctionValue.h" 37 #include "core/css/Pair.h" 38 #include "core/css/Rect.h" 39 #include "core/css/resolver/StyleResolverState.h" 40 #include "core/rendering/style/BorderImageLengthBox.h" 41 #include "core/rendering/style/FillLayer.h" 42 43 namespace blink { 44 45 const CSSToLengthConversionData& CSSToStyleMap::cssToLengthConversionData() const 46 { 47 return m_state.cssToLengthConversionData(); 48 } 49 50 PassRefPtr<StyleImage> CSSToStyleMap::styleImage(CSSPropertyID propertyId, CSSValue* value) 51 { 52 return m_elementStyleResources.styleImage(m_state.document(), m_state.document().textLinkColors(), m_state.style()->color(), propertyId, value); 53 } 54 55 void CSSToStyleMap::mapFillAttachment(FillLayer* layer, CSSValue* value) const 56 { 57 if (value->isInitialValue()) { 58 layer->setAttachment(FillLayer::initialFillAttachment(layer->type())); 59 return; 60 } 61 62 if (!value->isPrimitiveValue()) 63 return; 64 65 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 66 switch (primitiveValue->getValueID()) { 67 case CSSValueFixed: 68 layer->setAttachment(FixedBackgroundAttachment); 69 break; 70 case CSSValueScroll: 71 layer->setAttachment(ScrollBackgroundAttachment); 72 break; 73 case CSSValueLocal: 74 layer->setAttachment(LocalBackgroundAttachment); 75 break; 76 default: 77 return; 78 } 79 } 80 81 void CSSToStyleMap::mapFillClip(FillLayer* layer, CSSValue* value) const 82 { 83 if (value->isInitialValue()) { 84 layer->setClip(FillLayer::initialFillClip(layer->type())); 85 return; 86 } 87 88 if (!value->isPrimitiveValue()) 89 return; 90 91 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 92 layer->setClip(*primitiveValue); 93 } 94 95 void CSSToStyleMap::mapFillComposite(FillLayer* layer, CSSValue* value) const 96 { 97 if (value->isInitialValue()) { 98 layer->setComposite(FillLayer::initialFillComposite(layer->type())); 99 return; 100 } 101 102 if (!value->isPrimitiveValue()) 103 return; 104 105 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 106 layer->setComposite(*primitiveValue); 107 } 108 109 void CSSToStyleMap::mapFillBlendMode(FillLayer* layer, CSSValue* value) const 110 { 111 if (value->isInitialValue()) { 112 layer->setBlendMode(FillLayer::initialFillBlendMode(layer->type())); 113 return; 114 } 115 116 if (!value->isPrimitiveValue()) 117 return; 118 119 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 120 layer->setBlendMode(*primitiveValue); 121 } 122 123 void CSSToStyleMap::mapFillOrigin(FillLayer* layer, CSSValue* value) const 124 { 125 if (value->isInitialValue()) { 126 layer->setOrigin(FillLayer::initialFillOrigin(layer->type())); 127 return; 128 } 129 130 if (!value->isPrimitiveValue()) 131 return; 132 133 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 134 layer->setOrigin(*primitiveValue); 135 } 136 137 138 void CSSToStyleMap::mapFillImage(FillLayer* layer, CSSValue* value) 139 { 140 if (value->isInitialValue()) { 141 layer->setImage(FillLayer::initialFillImage(layer->type())); 142 return; 143 } 144 145 CSSPropertyID property = layer->type() == BackgroundFillLayer ? CSSPropertyBackgroundImage : CSSPropertyWebkitMaskImage; 146 layer->setImage(styleImage(property, value)); 147 } 148 149 void CSSToStyleMap::mapFillRepeatX(FillLayer* layer, CSSValue* value) const 150 { 151 if (value->isInitialValue()) { 152 layer->setRepeatX(FillLayer::initialFillRepeatX(layer->type())); 153 return; 154 } 155 156 if (!value->isPrimitiveValue()) 157 return; 158 159 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 160 layer->setRepeatX(*primitiveValue); 161 } 162 163 void CSSToStyleMap::mapFillRepeatY(FillLayer* layer, CSSValue* value) const 164 { 165 if (value->isInitialValue()) { 166 layer->setRepeatY(FillLayer::initialFillRepeatY(layer->type())); 167 return; 168 } 169 170 if (!value->isPrimitiveValue()) 171 return; 172 173 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 174 layer->setRepeatY(*primitiveValue); 175 } 176 177 void CSSToStyleMap::mapFillSize(FillLayer* layer, CSSValue* value) const 178 { 179 if (value->isInitialValue()) { 180 layer->setSizeType(FillLayer::initialFillSizeType(layer->type())); 181 layer->setSizeLength(FillLayer::initialFillSizeLength(layer->type())); 182 return; 183 } 184 185 if (!value->isPrimitiveValue()) 186 return; 187 188 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 189 if (primitiveValue->getValueID() == CSSValueContain) 190 layer->setSizeType(Contain); 191 else if (primitiveValue->getValueID() == CSSValueCover) 192 layer->setSizeType(Cover); 193 else 194 layer->setSizeType(SizeLength); 195 196 LengthSize b = FillLayer::initialFillSizeLength(layer->type()); 197 198 if (primitiveValue->getValueID() == CSSValueContain || primitiveValue->getValueID() == CSSValueCover) { 199 layer->setSizeLength(b); 200 return; 201 } 202 203 Length firstLength; 204 Length secondLength; 205 206 if (Pair* pair = primitiveValue->getPairValue()) { 207 firstLength = pair->first()->convertToLength<AnyConversion>(cssToLengthConversionData()); 208 secondLength = pair->second()->convertToLength<AnyConversion>(cssToLengthConversionData()); 209 } else { 210 firstLength = primitiveValue->convertToLength<AnyConversion>(cssToLengthConversionData()); 211 secondLength = Length(); 212 } 213 214 b.setWidth(firstLength); 215 b.setHeight(secondLength); 216 layer->setSizeLength(b); 217 } 218 219 void CSSToStyleMap::mapFillXPosition(FillLayer* layer, CSSValue* value) const 220 { 221 if (value->isInitialValue()) { 222 layer->setXPosition(FillLayer::initialFillXPosition(layer->type())); 223 return; 224 } 225 226 if (!value->isPrimitiveValue()) 227 return; 228 229 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 230 Pair* pair = primitiveValue->getPairValue(); 231 if (pair) 232 primitiveValue = pair->second(); 233 234 Length length = primitiveValue->convertToLength<FixedConversion | PercentConversion>(cssToLengthConversionData()); 235 236 layer->setXPosition(length); 237 if (pair) 238 layer->setBackgroundXOrigin(*(pair->first())); 239 } 240 241 void CSSToStyleMap::mapFillYPosition(FillLayer* layer, CSSValue* value) const 242 { 243 if (value->isInitialValue()) { 244 layer->setYPosition(FillLayer::initialFillYPosition(layer->type())); 245 return; 246 } 247 248 if (!value->isPrimitiveValue()) 249 return; 250 251 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 252 Pair* pair = primitiveValue->getPairValue(); 253 if (pair) 254 primitiveValue = pair->second(); 255 256 Length length = primitiveValue->convertToLength<FixedConversion | PercentConversion>(cssToLengthConversionData()); 257 258 layer->setYPosition(length); 259 if (pair) 260 layer->setBackgroundYOrigin(*(pair->first())); 261 } 262 263 void CSSToStyleMap::mapFillMaskSourceType(FillLayer* layer, CSSValue* value) const 264 { 265 EMaskSourceType type = FillLayer::initialFillMaskSourceType(layer->type()); 266 if (value->isInitialValue()) { 267 layer->setMaskSourceType(type); 268 return; 269 } 270 271 if (!value->isPrimitiveValue()) 272 return; 273 274 switch (toCSSPrimitiveValue(value)->getValueID()) { 275 case CSSValueAlpha: 276 type = MaskAlpha; 277 break; 278 case CSSValueLuminance: 279 type = MaskLuminance; 280 break; 281 case CSSValueAuto: 282 break; 283 default: 284 ASSERT_NOT_REACHED(); 285 } 286 287 layer->setMaskSourceType(type); 288 } 289 290 double CSSToStyleMap::mapAnimationDelay(CSSValue* value) 291 { 292 if (value->isInitialValue()) 293 return CSSTimingData::initialDelay(); 294 return toCSSPrimitiveValue(value)->computeSeconds(); 295 } 296 297 Timing::PlaybackDirection CSSToStyleMap::mapAnimationDirection(CSSValue* value) 298 { 299 if (value->isInitialValue()) 300 return CSSAnimationData::initialDirection(); 301 302 switch (toCSSPrimitiveValue(value)->getValueID()) { 303 case CSSValueNormal: 304 return Timing::PlaybackDirectionNormal; 305 case CSSValueAlternate: 306 return Timing::PlaybackDirectionAlternate; 307 case CSSValueReverse: 308 return Timing::PlaybackDirectionReverse; 309 case CSSValueAlternateReverse: 310 return Timing::PlaybackDirectionAlternateReverse; 311 default: 312 ASSERT_NOT_REACHED(); 313 return CSSAnimationData::initialDirection(); 314 } 315 } 316 317 double CSSToStyleMap::mapAnimationDuration(CSSValue* value) 318 { 319 if (value->isInitialValue()) 320 return CSSTimingData::initialDuration(); 321 return toCSSPrimitiveValue(value)->computeSeconds(); 322 } 323 324 Timing::FillMode CSSToStyleMap::mapAnimationFillMode(CSSValue* value) 325 { 326 if (value->isInitialValue()) 327 return CSSAnimationData::initialFillMode(); 328 329 switch (toCSSPrimitiveValue(value)->getValueID()) { 330 case CSSValueNone: 331 return Timing::FillModeNone; 332 case CSSValueForwards: 333 return Timing::FillModeForwards; 334 case CSSValueBackwards: 335 return Timing::FillModeBackwards; 336 case CSSValueBoth: 337 return Timing::FillModeBoth; 338 default: 339 ASSERT_NOT_REACHED(); 340 return CSSAnimationData::initialFillMode(); 341 } 342 } 343 344 double CSSToStyleMap::mapAnimationIterationCount(CSSValue* value) 345 { 346 if (value->isInitialValue()) 347 return CSSAnimationData::initialIterationCount(); 348 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 349 if (primitiveValue->getValueID() == CSSValueInfinite) 350 return std::numeric_limits<double>::infinity(); 351 return primitiveValue->getFloatValue(); 352 } 353 354 AtomicString CSSToStyleMap::mapAnimationName(CSSValue* value) 355 { 356 if (value->isInitialValue()) 357 return CSSAnimationData::initialName(); 358 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 359 if (primitiveValue->getValueID() == CSSValueNone) 360 return CSSAnimationData::initialName(); 361 return AtomicString(primitiveValue->getStringValue()); 362 } 363 364 EAnimPlayState CSSToStyleMap::mapAnimationPlayState(CSSValue* value) 365 { 366 if (value->isInitialValue()) 367 return CSSAnimationData::initialPlayState(); 368 if (toCSSPrimitiveValue(value)->getValueID() == CSSValuePaused) 369 return AnimPlayStatePaused; 370 ASSERT(toCSSPrimitiveValue(value)->getValueID() == CSSValueRunning); 371 return AnimPlayStatePlaying; 372 } 373 374 CSSTransitionData::TransitionProperty CSSToStyleMap::mapAnimationProperty(CSSValue* value) 375 { 376 if (value->isInitialValue()) 377 return CSSTransitionData::initialProperty(); 378 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 379 if (primitiveValue->isString()) 380 return CSSTransitionData::TransitionProperty(primitiveValue->getStringValue()); 381 if (primitiveValue->getValueID() == CSSValueAll) 382 return CSSTransitionData::TransitionProperty(CSSTransitionData::TransitionAll); 383 if (primitiveValue->getValueID() == CSSValueNone) 384 return CSSTransitionData::TransitionProperty(CSSTransitionData::TransitionNone); 385 return CSSTransitionData::TransitionProperty(primitiveValue->getPropertyID()); 386 } 387 388 PassRefPtr<TimingFunction> CSSToStyleMap::mapAnimationTimingFunction(CSSValue* value, bool allowStepMiddle) 389 { 390 // FIXME: We should probably only call into this function with a valid 391 // single timing function value which isn't initial or inherit. We can 392 // currently get into here with initial since the parser expands unset 393 // properties in shorthands to initial. 394 395 if (value->isPrimitiveValue()) { 396 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 397 switch (primitiveValue->getValueID()) { 398 case CSSValueLinear: 399 return LinearTimingFunction::shared(); 400 case CSSValueEase: 401 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease); 402 case CSSValueEaseIn: 403 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); 404 case CSSValueEaseOut: 405 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); 406 case CSSValueEaseInOut: 407 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut); 408 case CSSValueStepStart: 409 return StepsTimingFunction::preset(StepsTimingFunction::Start); 410 case CSSValueStepMiddle: 411 if (allowStepMiddle) 412 return StepsTimingFunction::preset(StepsTimingFunction::Middle); 413 return CSSTimingData::initialTimingFunction(); 414 case CSSValueStepEnd: 415 return StepsTimingFunction::preset(StepsTimingFunction::End); 416 default: 417 ASSERT_NOT_REACHED(); 418 return CSSTimingData::initialTimingFunction(); 419 } 420 } 421 422 if (value->isCubicBezierTimingFunctionValue()) { 423 CSSCubicBezierTimingFunctionValue* cubicTimingFunction = toCSSCubicBezierTimingFunctionValue(value); 424 return CubicBezierTimingFunction::create(cubicTimingFunction->x1(), cubicTimingFunction->y1(), cubicTimingFunction->x2(), cubicTimingFunction->y2()); 425 } 426 427 if (value->isInitialValue()) 428 return CSSTimingData::initialTimingFunction(); 429 430 CSSStepsTimingFunctionValue* stepsTimingFunction = toCSSStepsTimingFunctionValue(value); 431 if (stepsTimingFunction->stepAtPosition() == StepsTimingFunction::Middle && !allowStepMiddle) 432 return CSSTimingData::initialTimingFunction(); 433 return StepsTimingFunction::create(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtPosition()); 434 } 435 436 void CSSToStyleMap::mapNinePieceImage(RenderStyle* mutableStyle, CSSPropertyID property, CSSValue* value, NinePieceImage& image) 437 { 438 // If we're not a value list, then we are "none" and don't need to alter the empty image at all. 439 if (!value || !value->isValueList()) 440 return; 441 442 // Retrieve the border image value. 443 CSSValueList* borderImage = toCSSValueList(value); 444 445 // Set the image (this kicks off the load). 446 CSSPropertyID imageProperty; 447 if (property == CSSPropertyWebkitBorderImage) 448 imageProperty = CSSPropertyBorderImageSource; 449 else if (property == CSSPropertyWebkitMaskBoxImage) 450 imageProperty = CSSPropertyWebkitMaskBoxImageSource; 451 else 452 imageProperty = property; 453 454 for (unsigned i = 0 ; i < borderImage->length() ; ++i) { 455 CSSValue* current = borderImage->item(i); 456 457 if (current->isImageValue() || current->isImageGeneratorValue() || current->isImageSetValue()) 458 image.setImage(styleImage(imageProperty, current)); 459 else if (current->isBorderImageSliceValue()) 460 mapNinePieceImageSlice(current, image); 461 else if (current->isValueList()) { 462 CSSValueList* slashList = toCSSValueList(current); 463 size_t length = slashList->length(); 464 // Map in the image slices. 465 if (length && slashList->item(0)->isBorderImageSliceValue()) 466 mapNinePieceImageSlice(slashList->item(0), image); 467 468 // Map in the border slices. 469 if (length > 1) 470 image.setBorderSlices(mapNinePieceImageQuad(slashList->item(1))); 471 472 // Map in the outset. 473 if (length > 2) 474 image.setOutset(mapNinePieceImageQuad(slashList->item(2))); 475 } else if (current->isPrimitiveValue()) { 476 // Set the appropriate rules for stretch/round/repeat of the slices. 477 mapNinePieceImageRepeat(current, image); 478 } 479 } 480 481 if (property == CSSPropertyWebkitBorderImage) { 482 // We have to preserve the legacy behavior of -webkit-border-image and make the border slices 483 // also set the border widths. We don't need to worry about percentages, since we don't even support 484 // those on real borders yet. 485 if (image.borderSlices().top().isLength() && image.borderSlices().top().length().isFixed()) 486 mutableStyle->setBorderTopWidth(image.borderSlices().top().length().value()); 487 if (image.borderSlices().right().isLength() && image.borderSlices().right().length().isFixed()) 488 mutableStyle->setBorderRightWidth(image.borderSlices().right().length().value()); 489 if (image.borderSlices().bottom().isLength() && image.borderSlices().bottom().length().isFixed()) 490 mutableStyle->setBorderBottomWidth(image.borderSlices().bottom().length().value()); 491 if (image.borderSlices().left().isLength() && image.borderSlices().left().length().isFixed()) 492 mutableStyle->setBorderLeftWidth(image.borderSlices().left().length().value()); 493 } 494 } 495 496 void CSSToStyleMap::mapNinePieceImageSlice(CSSValue* value, NinePieceImage& image) const 497 { 498 if (!value || !value->isBorderImageSliceValue()) 499 return; 500 501 // Retrieve the border image value. 502 CSSBorderImageSliceValue* borderImageSlice = toCSSBorderImageSliceValue(value); 503 504 // Set up a length box to represent our image slices. 505 LengthBox box; 506 Quad* slices = borderImageSlice->slices(); 507 if (slices->top()->isPercentage()) 508 box.m_top = Length(slices->top()->getDoubleValue(), Percent); 509 else 510 box.m_top = Length(slices->top()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed); 511 if (slices->bottom()->isPercentage()) 512 box.m_bottom = Length(slices->bottom()->getDoubleValue(), Percent); 513 else 514 box.m_bottom = Length((int)slices->bottom()->getFloatValue(CSSPrimitiveValue::CSS_NUMBER), Fixed); 515 if (slices->left()->isPercentage()) 516 box.m_left = Length(slices->left()->getDoubleValue(), Percent); 517 else 518 box.m_left = Length(slices->left()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed); 519 if (slices->right()->isPercentage()) 520 box.m_right = Length(slices->right()->getDoubleValue(), Percent); 521 else 522 box.m_right = Length(slices->right()->getIntValue(CSSPrimitiveValue::CSS_NUMBER), Fixed); 523 image.setImageSlices(box); 524 525 // Set our fill mode. 526 image.setFill(borderImageSlice->m_fill); 527 } 528 529 static BorderImageLength toBorderImageLength(CSSPrimitiveValue& value, const CSSToLengthConversionData& conversionData) 530 { 531 if (value.isNumber()) 532 return value.getDoubleValue(); 533 if (value.isPercentage()) 534 return Length(value.getDoubleValue(CSSPrimitiveValue::CSS_PERCENTAGE), Percent); 535 if (value.getValueID() != CSSValueAuto) 536 return value.computeLength<Length>(conversionData); 537 return Length(Auto); 538 } 539 540 BorderImageLengthBox CSSToStyleMap::mapNinePieceImageQuad(CSSValue* value) const 541 { 542 if (!value || !value->isPrimitiveValue()) 543 return BorderImageLengthBox(Length(Auto)); 544 545 Quad* slices = toCSSPrimitiveValue(value)->getQuadValue(); 546 547 // Set up a border image length box to represent our image slices. 548 return BorderImageLengthBox( 549 toBorderImageLength(*slices->top(), cssToLengthConversionData()), 550 toBorderImageLength(*slices->right(), cssToLengthConversionData()), 551 toBorderImageLength(*slices->bottom(), cssToLengthConversionData()), 552 toBorderImageLength(*slices->left(), cssToLengthConversionData())); 553 } 554 555 void CSSToStyleMap::mapNinePieceImageRepeat(CSSValue* value, NinePieceImage& image) const 556 { 557 if (!value || !value->isPrimitiveValue()) 558 return; 559 560 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); 561 Pair* pair = primitiveValue->getPairValue(); 562 if (!pair || !pair->first() || !pair->second()) 563 return; 564 565 CSSValueID firstIdentifier = pair->first()->getValueID(); 566 CSSValueID secondIdentifier = pair->second()->getValueID(); 567 568 ENinePieceImageRule horizontalRule; 569 switch (firstIdentifier) { 570 case CSSValueStretch: 571 horizontalRule = StretchImageRule; 572 break; 573 case CSSValueRound: 574 horizontalRule = RoundImageRule; 575 break; 576 case CSSValueSpace: 577 horizontalRule = SpaceImageRule; 578 break; 579 default: // CSSValueRepeat 580 horizontalRule = RepeatImageRule; 581 break; 582 } 583 image.setHorizontalRule(horizontalRule); 584 585 ENinePieceImageRule verticalRule; 586 switch (secondIdentifier) { 587 case CSSValueStretch: 588 verticalRule = StretchImageRule; 589 break; 590 case CSSValueRound: 591 verticalRule = RoundImageRule; 592 break; 593 case CSSValueSpace: 594 verticalRule = SpaceImageRule; 595 break; 596 default: // CSSValueRepeat 597 verticalRule = RepeatImageRule; 598 break; 599 } 600 image.setVerticalRule(verticalRule); 601 } 602 603 }; 604