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