1/* 2 * This file is part of the WebKit project. 3 * 4 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@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 37namespace WebCore { 38 39// Helper function 40static 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 72SVGCharacterLayoutInfo::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 98bool SVGCharacterLayoutInfo::xValueAvailable() const 99{ 100 return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size(); 101} 102 103bool SVGCharacterLayoutInfo::yValueAvailable() const 104{ 105 return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size(); 106} 107 108bool SVGCharacterLayoutInfo::dxValueAvailable() const 109{ 110 return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size(); 111} 112 113bool SVGCharacterLayoutInfo::dyValueAvailable() const 114{ 115 return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size(); 116} 117 118bool SVGCharacterLayoutInfo::angleValueAvailable() const 119{ 120 return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size(); 121} 122 123bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const 124{ 125 return !baselineShiftStack.isEmpty(); 126} 127 128float SVGCharacterLayoutInfo::xValueNext() const 129{ 130 ASSERT(!xStack.isEmpty()); 131 return xStack.last().valueAtCurrentPosition(); 132} 133 134float SVGCharacterLayoutInfo::yValueNext() const 135{ 136 ASSERT(!yStack.isEmpty()); 137 return yStack.last().valueAtCurrentPosition(); 138} 139 140float SVGCharacterLayoutInfo::dxValueNext() const 141{ 142 ASSERT(!dxStack.isEmpty()); 143 return dxStack.last().valueAtCurrentPosition(); 144} 145 146float SVGCharacterLayoutInfo::dyValueNext() const 147{ 148 ASSERT(!dyStack.isEmpty()); 149 return dyStack.last().valueAtCurrentPosition(); 150} 151 152float SVGCharacterLayoutInfo::angleValueNext() const 153{ 154 ASSERT(!angleStack.isEmpty()); 155 return angleStack.last().valueAtCurrentPosition(); 156} 157 158float SVGCharacterLayoutInfo::baselineShiftValueNext() const 159{ 160 ASSERT(!baselineShiftStack.isEmpty()); 161 return baselineShiftStack.last(); 162} 163 164void SVGCharacterLayoutInfo::processedSingleCharacter() 165{ 166 xStackWalk(); 167 yStackWalk(); 168 dxStackWalk(); 169 dyStackWalk(); 170 angleStackWalk(); 171 baselineShiftStackWalk(); 172} 173 174void 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 225bool 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 256bool SVGCharacterLayoutInfo::inPathLayout() const 257{ 258 return pathLayout; 259} 260 261void SVGCharacterLayoutInfo::setInPathLayout(bool value) 262{ 263 pathLayout = value; 264 265 pathExtraAdvance = 0.0f; 266 pathTextLength = 0.0f; 267 pathChunkLength = 0.0f; 268} 269 270void 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 309void 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 335void 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 355void 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 374void 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 402void 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 417void 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 434void 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 451void 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 468void 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 485void 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 502void SVGCharacterLayoutInfo::baselineShiftStackWalk() 503{ 504 if (!baselineShiftStack.isEmpty()) { 505 baselineShiftStack.removeLast(); 506 baselineShiftStackChanged = false; 507 } 508} 509 510bool SVGChar::isHidden() const 511{ 512 return pathData && pathData->hidden; 513} 514 515AffineTransform 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