File indexing completed on 2024-05-05 12:16:14
0001 /* 0002 * This file is part of the WebKit project. 0003 * 0004 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> 0005 * 0006 * This library is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU Library General Public 0008 * License as published by the Free Software Foundation; either 0009 * version 2 of the License, or (at your option) any later version. 0010 * 0011 * This library is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 * Library General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Library General Public License 0017 * along with this library; see the file COPYING.LIB. If not, write to 0018 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 * Boston, MA 02110-1301, USA. 0020 * 0021 */ 0022 0023 #include "wtf/Platform.h" 0024 0025 #if ENABLE(SVG) 0026 #include "SVGCharacterLayoutInfo.h" 0027 0028 //#include "InlineFlowBox.h" 0029 //#include "InlineTextBox.h" 0030 #include "SVGLengthList.h" 0031 #include "SVGNumberList.h" 0032 #include "SVGTextPositioningElement.h" 0033 #include "RenderSVGTextPath.h" 0034 0035 #include <float.h> 0036 0037 0038 namespace WebCore 0039 { 0040 using namespace khtml; 0041 using namespace DOM; 0042 0043 // Helper function 0044 static float calculateBaselineShift(RenderObject *item) 0045 { 0046 const Font &font = item->style()->htmlFont(); 0047 const SVGRenderStyle *svgStyle = item->style()->svgStyle(); 0048 0049 float baselineShift = 0.0f; 0050 if (svgStyle->baselineShift() == BS_LENGTH) { 0051 CSSPrimitiveValueImpl *primitive = static_cast<CSSPrimitiveValueImpl *>(svgStyle->baselineShiftValue()); 0052 baselineShift = primitive->floatValue(); 0053 0054 /*FIXME khtml if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE) 0055 baselineShift = baselineShift / 100.0f * font.pixelSize();*/ 0056 } else { 0057 float baselineAscent = font.ascent() + font.descent(); 0058 0059 switch (svgStyle->baselineShift()) { 0060 case BS_BASELINE: 0061 break; 0062 case BS_SUB: 0063 baselineShift = -baselineAscent / 2.0f; 0064 break; 0065 case BS_SUPER: 0066 baselineShift = baselineAscent / 2.0f; 0067 break; 0068 default: 0069 ASSERT_NOT_REACHED(); 0070 } 0071 } 0072 0073 return baselineShift; 0074 } 0075 0076 SVGCharacterLayoutInfo::SVGCharacterLayoutInfo(Vector<SVGChar> &chars) 0077 : curx(0.0f) 0078 , cury(0.0f) 0079 , angle(0.0f) 0080 , dx(0.0f) 0081 , dy(0.0f) 0082 , shiftx(0.0f) 0083 , shifty(0.0f) 0084 , pathExtraAdvance(0.0f) 0085 , pathTextLength(0.0f) 0086 , pathChunkLength(0.0f) 0087 , svgChars(chars) 0088 , nextDrawnSeperated(false) 0089 , xStackChanged(false) 0090 , yStackChanged(false) 0091 , dxStackChanged(false) 0092 , dyStackChanged(false) 0093 , angleStackChanged(false) 0094 , baselineShiftStackChanged(false) 0095 , pathLayout(false) 0096 , currentOffset(0.0f) 0097 , startOffset(0.0f) 0098 , layoutPathLength(0.0f) 0099 { 0100 } 0101 0102 bool SVGCharacterLayoutInfo::xValueAvailable() const 0103 { 0104 return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size(); 0105 } 0106 0107 bool SVGCharacterLayoutInfo::yValueAvailable() const 0108 { 0109 return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size(); 0110 } 0111 0112 bool SVGCharacterLayoutInfo::dxValueAvailable() const 0113 { 0114 return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size(); 0115 } 0116 0117 bool SVGCharacterLayoutInfo::dyValueAvailable() const 0118 { 0119 return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size(); 0120 } 0121 0122 bool SVGCharacterLayoutInfo::angleValueAvailable() const 0123 { 0124 return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size(); 0125 } 0126 0127 bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const 0128 { 0129 return !baselineShiftStack.isEmpty(); 0130 } 0131 0132 float SVGCharacterLayoutInfo::xValueNext() const 0133 { 0134 ASSERT(!xStack.isEmpty()); 0135 return xStack.last().valueAtCurrentPosition(); 0136 } 0137 0138 float SVGCharacterLayoutInfo::yValueNext() const 0139 { 0140 ASSERT(!yStack.isEmpty()); 0141 return yStack.last().valueAtCurrentPosition(); 0142 } 0143 0144 float SVGCharacterLayoutInfo::dxValueNext() const 0145 { 0146 ASSERT(!dxStack.isEmpty()); 0147 return dxStack.last().valueAtCurrentPosition(); 0148 } 0149 0150 float SVGCharacterLayoutInfo::dyValueNext() const 0151 { 0152 ASSERT(!dyStack.isEmpty()); 0153 return dyStack.last().valueAtCurrentPosition(); 0154 } 0155 0156 float SVGCharacterLayoutInfo::angleValueNext() const 0157 { 0158 ASSERT(!angleStack.isEmpty()); 0159 return angleStack.last().valueAtCurrentPosition(); 0160 } 0161 0162 float SVGCharacterLayoutInfo::baselineShiftValueNext() const 0163 { 0164 ASSERT(!baselineShiftStack.isEmpty()); 0165 return baselineShiftStack.last(); 0166 } 0167 0168 void SVGCharacterLayoutInfo::processedSingleCharacter() 0169 { 0170 xStackWalk(); 0171 yStackWalk(); 0172 dxStackWalk(); 0173 dyStackWalk(); 0174 angleStackWalk(); 0175 baselineShiftStackWalk(); 0176 } 0177 0178 void SVGCharacterLayoutInfo::processedChunk(float savedShiftX, float savedShiftY) 0179 { 0180 // baseline-shift doesn't span across ancestors, unlike dx/dy. 0181 curx += savedShiftX - shiftx; 0182 cury += savedShiftY - shifty; 0183 0184 if (inPathLayout()) { 0185 shiftx = savedShiftX; 0186 shifty = savedShiftY; 0187 } 0188 0189 // rotation also doesn't span 0190 angle = 0.0f; 0191 0192 if (xStackChanged) { 0193 ASSERT(!xStack.isEmpty()); 0194 xStack.removeLast(); 0195 xStackChanged = false; 0196 } 0197 0198 if (yStackChanged) { 0199 ASSERT(!yStack.isEmpty()); 0200 yStack.removeLast(); 0201 yStackChanged = false; 0202 } 0203 0204 if (dxStackChanged) { 0205 ASSERT(!dxStack.isEmpty()); 0206 dxStack.removeLast(); 0207 dxStackChanged = false; 0208 } 0209 0210 if (dyStackChanged) { 0211 ASSERT(!dyStack.isEmpty()); 0212 dyStack.removeLast(); 0213 dyStackChanged = false; 0214 } 0215 0216 if (angleStackChanged) { 0217 ASSERT(!angleStack.isEmpty()); 0218 angleStack.removeLast(); 0219 angleStackChanged = false; 0220 } 0221 0222 if (baselineShiftStackChanged) { 0223 ASSERT(!baselineShiftStack.isEmpty()); 0224 baselineShiftStack.removeLast(); 0225 baselineShiftStackChanged = false; 0226 } 0227 } 0228 0229 bool SVGCharacterLayoutInfo::nextPathLayoutPointAndAngle(float glyphAdvance, float extraAdvance, float newOffset) 0230 { 0231 if (layoutPathLength <= 0.0f) { 0232 return false; 0233 } 0234 0235 if (newOffset != FLT_MIN) { 0236 currentOffset = startOffset + newOffset; 0237 } 0238 0239 // Respect translation along path (extraAdvance is orthogonal to the path) 0240 currentOffset += extraAdvance; 0241 0242 float offset = currentOffset + glyphAdvance / 2.0f; 0243 currentOffset += glyphAdvance + pathExtraAdvance; 0244 0245 if (offset < 0.0f || offset > layoutPathLength) { 0246 return false; 0247 } 0248 0249 bool ok = false; 0250 FloatPoint point = layoutPath.pointAtLength(offset, ok); 0251 ASSERT(ok); 0252 0253 curx = point.x(); 0254 cury = point.y(); 0255 0256 angle = layoutPath.normalAngleAtLength(offset, ok); 0257 ASSERT(ok); 0258 0259 // fprintf(stderr, "t: %f, x: %f, y: %f, angle: %f, glyphAdvance: %f\n", currentOffset, x, y, angle, glyphAdvance); 0260 return true; 0261 } 0262 0263 bool SVGCharacterLayoutInfo::inPathLayout() const 0264 { 0265 return pathLayout; 0266 } 0267 0268 void SVGCharacterLayoutInfo::setInPathLayout(bool value) 0269 { 0270 pathLayout = value; 0271 0272 pathExtraAdvance = 0.0f; 0273 pathTextLength = 0.0f; 0274 pathChunkLength = 0.0f; 0275 } 0276 0277 void SVGCharacterLayoutInfo::addLayoutInformation(InlineFlowBox *flowBox, float textAnchorStartOffset) 0278 { 0279 bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() && 0280 dxStack.isEmpty() && dyStack.isEmpty() && 0281 angleStack.isEmpty() && baselineShiftStack.isEmpty() && 0282 curx == 0.0f && cury == 0.0f; 0283 0284 RenderSVGTextPath *textPath = static_cast<RenderSVGTextPath *>(flowBox->object()); 0285 Path path = textPath->layoutPath(); 0286 0287 float baselineShift = calculateBaselineShift(textPath); 0288 0289 layoutPath = path; 0290 layoutPathLength = path.length(); 0291 0292 if (layoutPathLength <= 0.0f) { 0293 return; 0294 } 0295 0296 startOffset = textPath->startOffset(); 0297 0298 if (textPath->startOffset() >= 0.0f && textPath->startOffset() <= 1.0f) { 0299 startOffset *= layoutPathLength; 0300 } 0301 0302 startOffset += textAnchorStartOffset; 0303 currentOffset = startOffset; 0304 0305 // Only baseline-shift is handled through the normal layout system 0306 addStackContent(BaselineShiftStack, baselineShift); 0307 0308 if (isInitialLayout) { 0309 xStackChanged = false; 0310 yStackChanged = false; 0311 dxStackChanged = false; 0312 dyStackChanged = false; 0313 angleStackChanged = false; 0314 baselineShiftStackChanged = false; 0315 } 0316 } 0317 0318 void SVGCharacterLayoutInfo::addLayoutInformation(SVGTextPositioningElement *element) 0319 { 0320 bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() && 0321 dxStack.isEmpty() && dyStack.isEmpty() && 0322 angleStack.isEmpty() && baselineShiftStack.isEmpty() && 0323 curx == 0.0f && cury == 0.0f; 0324 0325 float baselineShift = calculateBaselineShift(element->renderer()); 0326 0327 addStackContent(XStack, element->x()); 0328 addStackContent(YStack, element->y()); 0329 addStackContent(DxStack, element->dx()); 0330 addStackContent(DyStack, element->dy()); 0331 addStackContent(AngleStack, element->rotate()); 0332 addStackContent(BaselineShiftStack, baselineShift); 0333 0334 if (isInitialLayout) { 0335 xStackChanged = false; 0336 yStackChanged = false; 0337 dxStackChanged = false; 0338 dyStackChanged = false; 0339 angleStackChanged = false; 0340 baselineShiftStackChanged = false; 0341 } 0342 } 0343 0344 void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGNumberList *list) 0345 { 0346 unsigned length = list->numberOfItems(); 0347 if (!length) { 0348 return; 0349 } 0350 0351 PositionedFloatVector newLayoutInfo; 0352 0353 // TODO: Convert more efficiently! 0354 ExceptionCode ec = 0; 0355 for (unsigned i = 0; i < length; ++i) { 0356 float value = list->getItem(i, ec); 0357 ASSERT(ec == 0); 0358 0359 newLayoutInfo.append(value); 0360 } 0361 0362 addStackContent(type, newLayoutInfo); 0363 } 0364 0365 void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGLengthList *list) 0366 { 0367 unsigned length = list->numberOfItems(); 0368 if (!length) { 0369 return; 0370 } 0371 0372 PositionedFloatVector newLayoutInfo; 0373 0374 ExceptionCode ec = 0; 0375 for (unsigned i = 0; i < length; ++i) { 0376 float value = list->getItem(i, ec).value(); 0377 ASSERT(ec == 0); 0378 0379 newLayoutInfo.append(value); 0380 } 0381 0382 addStackContent(type, newLayoutInfo); 0383 } 0384 0385 void SVGCharacterLayoutInfo::addStackContent(StackType type, const PositionedFloatVector &list) 0386 { 0387 switch (type) { 0388 case XStack: 0389 xStackChanged = true; 0390 xStack.append(list); 0391 break; 0392 case YStack: 0393 yStackChanged = true; 0394 yStack.append(list); 0395 break; 0396 case DxStack: 0397 dxStackChanged = true; 0398 dxStack.append(list); 0399 break; 0400 case DyStack: 0401 dyStackChanged = true; 0402 dyStack.append(list); 0403 break; 0404 case AngleStack: 0405 angleStackChanged = true; 0406 angleStack.append(list); 0407 break; 0408 default: 0409 ASSERT_NOT_REACHED(); 0410 } 0411 } 0412 0413 void SVGCharacterLayoutInfo::addStackContent(StackType type, float value) 0414 { 0415 if (value == 0.0f) { 0416 return; 0417 } 0418 0419 switch (type) { 0420 case BaselineShiftStack: 0421 baselineShiftStackChanged = true; 0422 baselineShiftStack.append(value); 0423 break; 0424 default: 0425 ASSERT_NOT_REACHED(); 0426 } 0427 } 0428 0429 void SVGCharacterLayoutInfo::xStackWalk() 0430 { 0431 unsigned i = 1; 0432 0433 while (!xStack.isEmpty()) { 0434 PositionedFloatVector &cur = xStack.last(); 0435 if (i + cur.position() < cur.size()) { 0436 cur.advance(i); 0437 break; 0438 } 0439 0440 i += cur.position(); 0441 xStack.removeLast(); 0442 xStackChanged = false; 0443 } 0444 } 0445 0446 void SVGCharacterLayoutInfo::yStackWalk() 0447 { 0448 unsigned i = 1; 0449 0450 while (!yStack.isEmpty()) { 0451 PositionedFloatVector &cur = yStack.last(); 0452 if (i + cur.position() < cur.size()) { 0453 cur.advance(i); 0454 break; 0455 } 0456 0457 i += cur.position(); 0458 yStack.removeLast(); 0459 yStackChanged = false; 0460 } 0461 } 0462 0463 void SVGCharacterLayoutInfo::dxStackWalk() 0464 { 0465 unsigned i = 1; 0466 0467 while (!dxStack.isEmpty()) { 0468 PositionedFloatVector &cur = dxStack.last(); 0469 if (i + cur.position() < cur.size()) { 0470 cur.advance(i); 0471 break; 0472 } 0473 0474 i += cur.position(); 0475 dxStack.removeLast(); 0476 dxStackChanged = false; 0477 } 0478 } 0479 0480 void SVGCharacterLayoutInfo::dyStackWalk() 0481 { 0482 unsigned i = 1; 0483 0484 while (!dyStack.isEmpty()) { 0485 PositionedFloatVector &cur = dyStack.last(); 0486 if (i + cur.position() < cur.size()) { 0487 cur.advance(i); 0488 break; 0489 } 0490 0491 i += cur.position(); 0492 dyStack.removeLast(); 0493 dyStackChanged = false; 0494 } 0495 } 0496 0497 void SVGCharacterLayoutInfo::angleStackWalk() 0498 { 0499 unsigned i = 1; 0500 0501 while (!angleStack.isEmpty()) { 0502 PositionedFloatVector &cur = angleStack.last(); 0503 if (i + cur.position() < cur.size()) { 0504 cur.advance(i); 0505 break; 0506 } 0507 0508 i += cur.position(); 0509 angleStack.removeLast(); 0510 angleStackChanged = false; 0511 } 0512 } 0513 0514 void SVGCharacterLayoutInfo::baselineShiftStackWalk() 0515 { 0516 if (!baselineShiftStack.isEmpty()) { 0517 baselineShiftStack.removeLast(); 0518 baselineShiftStackChanged = false; 0519 } 0520 } 0521 0522 bool SVGChar::isHidden() const 0523 { 0524 return pathData && pathData->hidden; 0525 } 0526 0527 AffineTransform SVGChar::characterTransform() const 0528 { 0529 AffineTransform ctm; 0530 0531 // Rotate character around angle, and possibly scale. 0532 ctm.translate(x, y); 0533 ctm.rotate(angle); 0534 0535 if (pathData) { 0536 ctm.scale(pathData->xScale, pathData->yScale); 0537 ctm.translate(pathData->xShift, pathData->yShift); 0538 ctm.rotate(pathData->orientationAngle); 0539 } 0540 0541 ctm.translate(orientationShiftX - x, orientationShiftY - y); 0542 return ctm; 0543 } 0544 0545 } // namespace WebCore 0546 0547 #endif // ENABLE(SVG)