Warning, file /frameworks/khtml/src/rendering/SVGRootInlineBox.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * This file is part of the WebKit project. 0003 * 0004 * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> 0005 * (C) 2006 Apple Computer Inc. 0006 * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> 0007 * 0008 * This library is free software; you can redistribute it and/or 0009 * modify it under the terms of the GNU Library General Public 0010 * License as published by the Free Software Foundation; either 0011 * version 2 of the License, or (at your option) any later version. 0012 * 0013 * This library is distributed in the hope that it will be useful, 0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 * Library General Public License for more details. 0017 * 0018 * You should have received a copy of the GNU Library General Public License 0019 * along with this library; see the file COPYING.LIB. If not, write to 0020 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0021 * Boston, MA 02110-1301, USA. 0022 * 0023 */ 0024 0025 #include "wtf/Platform.h" 0026 0027 #if ENABLE(SVG) 0028 #include "SVGRootInlineBox.h" 0029 0030 /*#include "Editor.h" 0031 #include "Frame.h" 0032 #include "GraphicsContext.h"*/ 0033 #include "RenderSVGRoot.h" 0034 #include "SVGInlineFlowBox.h" 0035 #include "SVGInlineTextBox.h" 0036 #include "SVGFontElement.h" 0037 #include "SVGPaintServer.h" 0038 #include "SVGRenderStyleDefs.h" 0039 #include "SVGRenderSupport.h" 0040 #include "SVGResourceFilter.h" 0041 #include "SVGTextPositioningElement.h" 0042 #include "SVGURIReference.h" 0043 //#include "Text.h" 0044 //#include "UnicodeRange.h" 0045 0046 #include <float.h> 0047 #include <math.h> 0048 0049 // Text chunk creation is complex and the whole process 0050 // can easily be traced by setting this variable > 0. 0051 #define DEBUG_CHUNK_BUILDING 0 0052 0053 namespace WebCore 0054 { 0055 0056 static inline bool isVerticalWritingMode(const SVGRenderStyle *style) 0057 { 0058 return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB; 0059 } 0060 0061 static inline EAlignmentBaseline dominantBaselineToShift(bool isVerticalText, const RenderObject *text, const Font &font) 0062 { 0063 ASSERT(text); 0064 0065 const SVGRenderStyle *style = text->style() ? text->style()->svgStyle() : nullptr; 0066 ASSERT(style); 0067 0068 const SVGRenderStyle *parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : nullptr; 0069 0070 EDominantBaseline baseline = style->dominantBaseline(); 0071 if (baseline == DB_AUTO) { 0072 if (isVerticalText) { 0073 baseline = DB_CENTRAL; 0074 } else { 0075 baseline = DB_ALPHABETIC; 0076 } 0077 } 0078 0079 switch (baseline) { 0080 case DB_USE_SCRIPT: 0081 // TODO: The dominant-baseline and the baseline-table components are set by 0082 // determining the predominant script of the character data content. 0083 return AB_ALPHABETIC; 0084 case DB_NO_CHANGE: { 0085 if (parentStyle) { 0086 return dominantBaselineToShift(isVerticalText, text->parent(), font); 0087 } 0088 0089 ASSERT_NOT_REACHED(); 0090 return AB_AUTO; 0091 } 0092 case DB_RESET_SIZE: { 0093 if (parentStyle) { 0094 return dominantBaselineToShift(isVerticalText, text->parent(), font); 0095 } 0096 0097 ASSERT_NOT_REACHED(); 0098 return AB_AUTO; 0099 } 0100 case DB_IDEOGRAPHIC: 0101 return AB_IDEOGRAPHIC; 0102 case DB_ALPHABETIC: 0103 return AB_ALPHABETIC; 0104 case DB_HANGING: 0105 return AB_HANGING; 0106 case DB_MATHEMATICAL: 0107 return AB_MATHEMATICAL; 0108 case DB_CENTRAL: 0109 return AB_CENTRAL; 0110 case DB_MIDDLE: 0111 return AB_MIDDLE; 0112 case DB_TEXT_AFTER_EDGE: 0113 return AB_TEXT_AFTER_EDGE; 0114 case DB_TEXT_BEFORE_EDGE: 0115 return AB_TEXT_BEFORE_EDGE; 0116 default: 0117 ASSERT_NOT_REACHED(); 0118 return AB_AUTO; 0119 } 0120 } 0121 0122 static inline float alignmentBaselineToShift(bool isVerticalText, const RenderObject *text, const Font &font) 0123 { 0124 ASSERT(text); 0125 0126 const SVGRenderStyle *style = text->style() ? text->style()->svgStyle() : nullptr; 0127 ASSERT(style); 0128 0129 const SVGRenderStyle *parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : nullptr; 0130 0131 EAlignmentBaseline baseline = style->alignmentBaseline(); 0132 if (baseline == AB_AUTO) { 0133 if (parentStyle && style->dominantBaseline() == DB_AUTO) { 0134 baseline = dominantBaselineToShift(isVerticalText, text->parent(), font); 0135 } else { 0136 baseline = dominantBaselineToShift(isVerticalText, text, font); 0137 } 0138 0139 ASSERT(baseline != AB_AUTO); 0140 } 0141 0142 // Note: https://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling 0143 switch (baseline) { 0144 case AB_BASELINE: { 0145 if (parentStyle) { 0146 return dominantBaselineToShift(isVerticalText, text->parent(), font); 0147 } 0148 0149 return 0.0f; 0150 } 0151 case AB_BEFORE_EDGE: 0152 case AB_TEXT_BEFORE_EDGE: 0153 return font.ascent(); 0154 case AB_MIDDLE: 0155 return font.xHeight() / 2.0f; 0156 case AB_CENTRAL: 0157 // Not needed, we're taking this into account already for vertical text! 0158 // return (font.ascent() - font.descent()) / 2.0f; 0159 return 0.0f; 0160 case AB_AFTER_EDGE: 0161 case AB_TEXT_AFTER_EDGE: 0162 case AB_IDEOGRAPHIC: 0163 return font.descent(); 0164 case AB_ALPHABETIC: 0165 return 0.0f; 0166 case AB_HANGING: 0167 return font.ascent() * 8.0f / 10.0f; 0168 case AB_MATHEMATICAL: 0169 return font.ascent() / 2.0f; 0170 default: 0171 ASSERT_NOT_REACHED(); 0172 return 0.0f; 0173 } 0174 } 0175 0176 static inline float glyphOrientationToAngle(const SVGRenderStyle *svgStyle, bool isVerticalText, const UChar &character) 0177 { 0178 Q_UNUSED(character); 0179 switch (isVerticalText ? svgStyle->glyphOrientationVertical() : svgStyle->glyphOrientationHorizontal()) { 0180 case GO_AUTO: { 0181 // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees. 0182 // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees. 0183 /*FIXME: khtml porting; unsigned int unicodeRange = findCharUnicodeRange(character); 0184 if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic) 0185 return 90.0f;*/ 0186 0187 return 0.0f; 0188 } 0189 case GO_90DEG: 0190 return 90.0f; 0191 case GO_180DEG: 0192 return 180.0f; 0193 case GO_270DEG: 0194 return 270.0f; 0195 case GO_0DEG: 0196 default: 0197 return 0.0f; 0198 } 0199 } 0200 0201 static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle) 0202 { 0203 return fabsf(fmodf(orientationAngle, 180.0f)) == 0.0f; 0204 } 0205 0206 static inline float calculateGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float orientationAngle, float glyphWidth, float glyphHeight, const Font &font, SVGChar &svgChar, float &xOrientationShift, float &yOrientationShift) 0207 { 0208 bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(orientationAngle); 0209 0210 // The function is based on spec requirements: 0211 // 0212 // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of 0213 // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph. 0214 // 0215 // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of 0216 // 180 degrees,then the current text position is incremented according to the horizontal metrics of the glyph. 0217 0218 // vertical orientation handling 0219 if (isVerticalText) { 0220 if (orientationAngle == 0.0f) { 0221 xOrientationShift = -glyphWidth / 2.0f; 0222 yOrientationShift = font.ascent(); 0223 } else if (orientationAngle == 90.0f) { 0224 xOrientationShift = -glyphHeight; 0225 yOrientationShift = font.descent(); 0226 svgChar.orientationShiftY = -font.ascent(); 0227 } else if (orientationAngle == 270.0f) { 0228 xOrientationShift = glyphHeight; 0229 yOrientationShift = font.descent(); 0230 svgChar.orientationShiftX = -glyphWidth; 0231 svgChar.orientationShiftY = -font.ascent(); 0232 } else if (orientationAngle == 180.0f) { 0233 yOrientationShift = font.ascent(); 0234 svgChar.orientationShiftX = -glyphWidth / 2.0f; 0235 svgChar.orientationShiftY = font.ascent() - font.descent(); 0236 } 0237 0238 // vertical advance calculation 0239 if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees) { 0240 return glyphWidth; 0241 } 0242 0243 return glyphHeight; 0244 } 0245 0246 // horizontal orientation handling 0247 if (orientationAngle == 90.0f) { 0248 xOrientationShift = glyphWidth / 2.0f; 0249 yOrientationShift = -font.descent(); 0250 svgChar.orientationShiftX = -glyphWidth / 2.0f - font.descent(); 0251 svgChar.orientationShiftY = font.descent(); 0252 } else if (orientationAngle == 270.0f) { 0253 xOrientationShift = -glyphWidth / 2.0f; 0254 yOrientationShift = -font.descent(); 0255 svgChar.orientationShiftX = -glyphWidth / 2.0f + font.descent(); 0256 svgChar.orientationShiftY = glyphHeight; 0257 } else if (orientationAngle == 180.0f) { 0258 xOrientationShift = glyphWidth / 2.0f; 0259 svgChar.orientationShiftX = -glyphWidth / 2.0f; 0260 svgChar.orientationShiftY = font.ascent() - font.descent(); 0261 } 0262 0263 // horizontal advance calculation 0264 if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees) { 0265 return glyphHeight; 0266 } 0267 0268 return glyphWidth; 0269 } 0270 0271 static inline void startTextChunk(SVGTextChunkLayoutInfo &info) 0272 { 0273 info.chunk.boxes.clear(); 0274 info.chunk.boxes.append(SVGInlineBoxCharacterRange()); 0275 0276 info.chunk.start = info.it; 0277 info.assignChunkProperties = true; 0278 } 0279 0280 static inline void closeTextChunk(SVGTextChunkLayoutInfo &info) 0281 { 0282 ASSERT(!info.chunk.boxes.last().isOpen()); 0283 ASSERT(info.chunk.boxes.last().isClosed()); 0284 0285 info.chunk.end = info.it; 0286 ASSERT(info.chunk.end >= info.chunk.start); 0287 0288 info.svgTextChunks.append(info.chunk); 0289 } 0290 0291 RenderSVGRoot *findSVGRootObject(RenderObject *start) 0292 { 0293 // Find associated root inline box 0294 while (start && !start->isSVGRoot()) { 0295 start = start->parent(); 0296 } 0297 0298 ASSERT(start); 0299 ASSERT(start->isSVGRoot()); 0300 0301 return static_cast<RenderSVGRoot *>(start); 0302 } 0303 0304 static inline FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar> &chars) 0305 { 0306 return topLeftPositionOfCharacterRange(chars.begin(), chars.end()); 0307 } 0308 0309 FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator it, Vector<SVGChar>::iterator end) 0310 { 0311 float lowX = FLT_MAX, lowY = FLT_MAX; 0312 for (; it != end; ++it) { 0313 if (it->isHidden()) { 0314 continue; 0315 } 0316 0317 float x = (*it).x; 0318 float y = (*it).y; 0319 0320 if (x < lowX) { 0321 lowX = x; 0322 } 0323 0324 if (y < lowY) { 0325 lowY = y; 0326 } 0327 } 0328 0329 return FloatPoint(lowX, lowY); 0330 } 0331 0332 /* FIXME 0333 // Helper function 0334 static float calculateKerning(RenderObject* item) 0335 { 0336 Q_UNUSED(item); 0337 const Font& font = item->style()->font(); 0338 const SVGRenderStyle* svgStyle = item->style()->svgStyle(); 0339 0340 float kerning = 0.0f; 0341 if (CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->kerning())) { 0342 kerning = primitive->getFloatValue(); 0343 0344 if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE && font.pixelSize() > 0) 0345 kerning = kerning / 100.0f * font.pixelSize(); 0346 } 0347 0348 return kerning; 0349 } 0350 */ 0351 0352 // Helper class for paint() 0353 struct SVGRootInlineBoxPaintWalker { 0354 SVGRootInlineBoxPaintWalker(SVGRootInlineBox *rootBox, SVGResourceFilter *rootFilter, RenderObject::PaintInfo paintInfo, int tx, int ty) 0355 : m_rootBox(rootBox) 0356 , m_chunkStarted(false) 0357 , m_paintInfo(paintInfo) 0358 , m_savedInfo(paintInfo) 0359 , m_boundingBox(tx + rootBox->xPos(), ty + rootBox->yPos(), rootBox->width(), rootBox->height()) 0360 , m_filter(nullptr) 0361 , m_rootFilter(rootFilter) 0362 , m_fillPaintServer(nullptr) 0363 , m_strokePaintServer(nullptr) 0364 , m_fillPaintServerObject(nullptr) 0365 , m_strokePaintServerObject(nullptr) 0366 , m_tx(tx) 0367 , m_ty(ty) 0368 { 0369 } 0370 0371 ~SVGRootInlineBoxPaintWalker() 0372 { 0373 ASSERT(!m_filter); 0374 ASSERT(!m_fillPaintServer); 0375 ASSERT(!m_fillPaintServerObject); 0376 ASSERT(!m_strokePaintServer); 0377 ASSERT(!m_strokePaintServerObject); 0378 ASSERT(!m_chunkStarted); 0379 } 0380 0381 void teardownFillPaintServer() 0382 { 0383 if (!m_fillPaintServer) { 0384 return; 0385 } 0386 0387 m_fillPaintServer->teardown(m_paintInfo.p, nullptr, m_fillPaintServerObject, ApplyToFillTargetType, true); 0388 0389 m_fillPaintServer = nullptr; 0390 m_fillPaintServerObject = nullptr; 0391 } 0392 0393 void teardownStrokePaintServer() 0394 { 0395 if (!m_strokePaintServer) { 0396 return; 0397 } 0398 0399 m_strokePaintServer->teardown(m_paintInfo.p, nullptr, m_strokePaintServerObject, ApplyToStrokeTargetType, true); 0400 0401 m_strokePaintServer = nullptr; 0402 m_strokePaintServerObject = nullptr; 0403 } 0404 0405 void chunkStartCallback(InlineBox *box) 0406 { 0407 ASSERT(!m_chunkStarted); 0408 m_chunkStarted = true; 0409 0410 InlineFlowBox *flowBox = box->parent(); 0411 0412 // Initialize text rendering 0413 RenderObject *object = flowBox->object(); 0414 ASSERT(object); 0415 0416 m_savedInfo = m_paintInfo; 0417 //m_paintInfo.context->save(); 0418 0419 if (!flowBox->isRootInlineBox()) { 0420 ;//FIXME m_paintInfo.context->concatCTM(m_rootBox->object()->localTransform()); 0421 } 0422 0423 //m_paintInfo.context->concatCTM(object->localTransform()); 0424 m_paintInfo.p->setWorldMatrix(object->localTransform(), true); 0425 0426 if (!flowBox->isRootInlineBox()) { 0427 prepareToRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_rootFilter); 0428 // FIXME khtml m_paintInfo.rect = object->localTransform().inverse().mapRect(m_paintInfo.rect); 0429 } 0430 } 0431 0432 void chunkEndCallback(InlineBox *box) 0433 { 0434 ASSERT(m_chunkStarted); 0435 m_chunkStarted = false; 0436 0437 InlineFlowBox *flowBox = box->parent(); 0438 0439 RenderObject *object = flowBox->object(); 0440 ASSERT(object); 0441 0442 // Clean up last used paint server 0443 teardownFillPaintServer(); 0444 teardownStrokePaintServer(); 0445 0446 // Finalize text rendering 0447 if (!flowBox->isRootInlineBox()) { 0448 //FIXME khtml finishRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_savedInfo.context); 0449 m_filter = nullptr; 0450 } 0451 0452 // Restore context & repaint rect 0453 //FIXME m_paintInfo.context->restore(); 0454 //FIXME m_paintInfo.rect = m_savedInfo.rect; 0455 } 0456 0457 bool chunkSetupFillCallback(InlineBox *box) 0458 { 0459 InlineFlowBox *flowBox = box->parent(); 0460 0461 // Setup fill paint server 0462 RenderObject *object = flowBox->object(); 0463 ASSERT(object); 0464 0465 ASSERT(!m_strokePaintServer); 0466 teardownFillPaintServer(); 0467 0468 m_fillPaintServer = SVGPaintServer::fillPaintServer(object->style(), object); 0469 if (m_fillPaintServer) { 0470 m_fillPaintServer->setup(m_paintInfo.p, nullptr, object, ApplyToFillTargetType, true); 0471 m_fillPaintServerObject = object; 0472 return true; 0473 } 0474 0475 return false; 0476 } 0477 0478 bool chunkSetupStrokeCallback(InlineBox *box) 0479 { 0480 InlineFlowBox *flowBox = box->parent(); 0481 0482 // Setup stroke paint server 0483 RenderObject *object = flowBox->object(); 0484 ASSERT(object); 0485 0486 // If we're both stroked & filled, teardown fill paint server before stroking. 0487 teardownFillPaintServer(); 0488 teardownStrokePaintServer(); 0489 0490 m_strokePaintServer = SVGPaintServer::strokePaintServer(object->style(), object); 0491 0492 if (m_strokePaintServer) { 0493 m_strokePaintServer->setup(m_paintInfo.p, nullptr, object, ApplyToStrokeTargetType, true); 0494 m_strokePaintServerObject = object; 0495 return true; 0496 } 0497 0498 return false; 0499 } 0500 0501 void chunkPortionCallback(SVGInlineTextBox *textBox, int startOffset, const AffineTransform &chunkCtm, 0502 const Vector<SVGChar>::iterator &start, const Vector<SVGChar>::iterator &end) 0503 { 0504 Q_UNUSED(chunkCtm); 0505 //qCDebug(KHTML_LOG) << "text chunk rendering code here"; 0506 RenderText *text = textBox->/*textObject()*/renderText(); 0507 ASSERT(text); 0508 0509 RenderStyle *styleToUse = text->style(/*textBox->isFirstLineStyle()*/); 0510 ASSERT(styleToUse); 0511 0512 startOffset += textBox->start(); 0513 0514 int textDecorations = styleToUse->textDecorationsInEffect(); 0515 0516 // khtml int textWidth = 0; 0517 IntPoint decorationOrigin; 0518 SVGTextDecorationInfo info; 0519 0520 /*FIXME khtml if (!chunkCtm.isIdentity()) 0521 m_paintInfo.context->concatCTM(chunkCtm);*/ 0522 0523 for (Vector<SVGChar>::iterator it = start; it != end; ++it) { 0524 if (it->isHidden()) { 0525 continue; 0526 } 0527 0528 // Determine how many characters - starting from the current - can be drawn at once. 0529 Vector<SVGChar>::iterator itSearch = it + 1; 0530 while (itSearch != end) { 0531 if (itSearch->drawnSeperated || itSearch->isHidden()) { 0532 break; 0533 } 0534 0535 itSearch++; 0536 } 0537 0538 /*FIXME khtmlconst*/ UChar *stringStart = text->text() + startOffset + (it - start); 0539 unsigned int stringLength = itSearch - it; 0540 0541 // Paint decorations, that have to be drawn before the text gets drawn 0542 if (textDecorations != TDNONE /*FIXME khtml && m_paintInfo.phase != PaintPhaseSelection*/) { 0543 //khtml textWidth = styleToUse->font().width(svgTextRunForInlineTextBox(stringStart, stringLength, styleToUse, textBox, (*it).x)); 0544 //FIXME khtml textWidth = styleToUse->htmlFont().width(stringStart, stringLength, 0, stringLength, false /*fast algo*/); 0545 decorationOrigin = IntPoint((int)(*it).x, (int)(*it).y - styleToUse->htmlFont().ascent()); 0546 info = m_rootBox->retrievePaintServersForTextDecoration(text); 0547 } 0548 0549 /*if (textDecorations & UNDERLINE && textWidth != 0.0f) 0550 textBox->paintDecoration(UNDERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); 0551 0552 if (textDecorations & OVERLINE && textWidth != 0.0f) 0553 textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);*/ 0554 0555 // Paint text 0556 SVGPaintServer *activePaintServer = m_fillPaintServer; 0557 if (!activePaintServer) { 0558 activePaintServer = m_strokePaintServer; 0559 } 0560 0561 ASSERT(activePaintServer); 0562 textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, activePaintServer); 0563 0564 // Paint decorations, that have to be drawn afterwards 0565 /* FIXME khtml if (textDecorations & LINE_THROUGH && textWidth != 0.0f) 0566 textBox->paintDecoration(LINE_THROUGH, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info);*/ 0567 0568 // Skip processed characters 0569 it = itSearch - 1; 0570 } 0571 0572 /* FIXME khtml if (!chunkCtm.isIdentity()) 0573 m_paintInfo.context->concatCTM(chunkCtm.inverse());*/ 0574 } 0575 0576 private: 0577 SVGRootInlineBox *m_rootBox; 0578 bool m_chunkStarted : 1; 0579 0580 RenderObject::PaintInfo m_paintInfo; 0581 RenderObject::PaintInfo m_savedInfo; 0582 0583 FloatRect m_boundingBox; 0584 SVGResourceFilter *m_filter; 0585 SVGResourceFilter *m_rootFilter; 0586 0587 SVGPaintServer *m_fillPaintServer; 0588 SVGPaintServer *m_strokePaintServer; 0589 0590 RenderObject *m_fillPaintServerObject; 0591 RenderObject *m_strokePaintServerObject; 0592 0593 int m_tx; 0594 int m_ty; 0595 }; 0596 0597 void SVGRootInlineBox::paint(RenderObject::PaintInfo &paintInfo, int tx, int ty) 0598 { 0599 //if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground) 0600 // return; 0601 0602 RenderObject::PaintInfo savedInfo(paintInfo); 0603 //paintInfo.context->save(); 0604 0605 SVGResourceFilter *filter = nullptr; 0606 FloatRect boundingBox(tx + xPos(), ty + yPos(), width(), height()); 0607 0608 // Initialize text rendering 0609 paintInfo.p->setWorldMatrix(object()->localTransform(), true); 0610 prepareToRenderSVGContent(object(), paintInfo, boundingBox, filter); 0611 paintInfo.p->setWorldMatrix(object()->localTransform().inverse(), true); 0612 0613 //qCDebug(KHTML_LOG) << "paint at" << tx << ty; 0614 //qCDebug(KHTML_LOG) << "pos: (" << (tx + xPos()) << "," << (ty + yPos()) << ")"; 0615 //qCDebug(KHTML_LOG) << "size: " << width() << "x" << height(); 0616 0617 // Render text, chunk-by-chunk 0618 SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty); 0619 SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback, 0620 &SVGRootInlineBoxPaintWalker::chunkPortionCallback, 0621 &SVGRootInlineBoxPaintWalker::chunkStartCallback, 0622 &SVGRootInlineBoxPaintWalker::chunkEndCallback, 0623 &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback, 0624 &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback); 0625 0626 walkTextChunks(&walker); 0627 0628 // Finalize text rendering 0629 //FIXME khtml finishRenderSVGContent(object(), paintInfo, boundingBox, filter, savedInfo.context); 0630 //paintInfo.context->restore(); 0631 } 0632 0633 int SVGRootInlineBox::placeBoxesHorizontally(int, int &leftPosition, int &rightPosition, bool &) 0634 { 0635 // Remove any offsets caused by RTL text layout 0636 leftPosition = 0; 0637 rightPosition = 0; 0638 return 0; 0639 } 0640 0641 void SVGRootInlineBox::verticallyAlignBoxes(int &heightOfBlock) 0642 { 0643 // height is set by layoutInlineBoxes. 0644 heightOfBlock = height(); 0645 } 0646 0647 float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange &range) 0648 { 0649 ASSERT(!range.isOpen()); 0650 ASSERT(range.isClosed()); 0651 ASSERT(range.box->isInlineTextBox()); 0652 0653 InlineTextBox *textBox = static_cast<InlineTextBox *>(range.box); 0654 RenderText *text = textBox->renderText(); 0655 RenderStyle *style = text->style(); 0656 0657 return style->htmlFont().floatWidth(text->text(), textBox->start() + range.startOffset, 0658 range.endOffset - range.startOffset); 0659 } 0660 0661 float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange &range) 0662 { 0663 ASSERT(!range.isOpen()); 0664 ASSERT(range.isClosed()); 0665 ASSERT(range.box->isInlineTextBox()); 0666 0667 InlineTextBox *textBox = static_cast<InlineTextBox *>(range.box); 0668 RenderText *text = textBox->renderText(); 0669 const khtml::Font &font = text->style()->htmlFont(); 0670 return (range.endOffset - range.startOffset) * (font.ascent() + font.descent()); 0671 return 0; 0672 } 0673 0674 /*TextRun svgTextRunForInlineTextBox(const UChar* c, int len, RenderStyle* style, const InlineTextBox* textBox, float xPos) 0675 { 0676 ASSERT(textBox); 0677 ASSERT(style); 0678 0679 TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->m_reversed, textBox->m_dirOverride || style->visuallyOrdered()); 0680 0681 #if ENABLE(SVG_FONTS) 0682 run.setReferencingRenderObject(textBox->textObject()->parent()); 0683 #endif 0684 0685 // We handle letter & word spacing ourselves 0686 run.disableSpacing(); 0687 return run; 0688 }*/ 0689 0690 static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk &chunk, bool calcWidthOnly) 0691 { 0692 float length = 0.0f; 0693 Vector<SVGChar>::iterator charIt = chunk.start; 0694 0695 Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin(); 0696 Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end(); 0697 0698 for (; it != end; ++it) { 0699 SVGInlineBoxCharacterRange &range = *it; 0700 // qCDebug(KHTML_LOG) << "box range:" << range.startOffset << range.endOffset; 0701 0702 SVGInlineTextBox *box = static_cast<SVGInlineTextBox *>(range.box); 0703 RenderStyle *style = box->object()->style(); 0704 0705 for (int i = range.startOffset; i < range.endOffset; ++i) { 0706 ASSERT(charIt <= chunk.end); 0707 0708 // Determine how many characters - starting from the current - can be measured at once. 0709 // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width 0710 // of a string is not the sum of the boundaries of all contained glyphs. 0711 Vector<SVGChar>::iterator itSearch = charIt + 1; 0712 Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i; 0713 while (itSearch != endSearch) { 0714 // No need to check for 'isHidden()' here as this function is not called for text paths. 0715 if (itSearch->drawnSeperated) { 0716 break; 0717 } 0718 0719 itSearch++; 0720 } 0721 0722 unsigned int positionOffset = itSearch - charIt; 0723 0724 // Calculate width/height of subrange 0725 SVGInlineBoxCharacterRange subRange; 0726 subRange.box = range.box; 0727 subRange.startOffset = i; 0728 subRange.endOffset = i + positionOffset; 0729 // qCDebug(KHTML_LOG) << "got subrange:" << subRange.startOffset << subRange.endOffset; 0730 0731 if (calcWidthOnly) { 0732 length += cummulatedWidthOfInlineBoxCharacterRange(subRange); 0733 } else { 0734 length += cummulatedHeightOfInlineBoxCharacterRange(subRange); 0735 } 0736 0737 // Calculate gap between the previous & current range 0738 // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account 0739 // so add "40" as width, and analogous for B & C, add "20" as width. 0740 if (itSearch > chunk.start && itSearch < chunk.end) { 0741 SVGChar &lastCharacter = *(itSearch - 1); 0742 SVGChar ¤tCharacter = *itSearch; 0743 0744 int offset = box->m_reversed ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1; 0745 0746 // FIXME: does this need to change to handle multichar glyphs? 0747 int charsConsumed = 1; 0748 String glyphName; 0749 if (calcWidthOnly) { 0750 float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName); 0751 length += currentCharacter.x - lastCharacter.x - lastGlyphWidth; 0752 } else { 0753 float lastGlyphHeight = box->calculateGlyphHeight(style, offset, 0); 0754 length += currentCharacter.y - lastCharacter.y - lastGlyphHeight; 0755 } 0756 } 0757 0758 // Advance processed characters 0759 i += positionOffset - 1; 0760 charIt = itSearch; 0761 } 0762 } 0763 0764 ASSERT(charIt == chunk.end); 0765 return length; 0766 } 0767 0768 static float cummulatedWidthOfTextChunk(SVGTextChunk &chunk) 0769 { 0770 return cummulatedWidthOrHeightOfTextChunk(chunk, true); 0771 } 0772 0773 static float cummulatedHeightOfTextChunk(SVGTextChunk &chunk) 0774 { 0775 return cummulatedWidthOrHeightOfTextChunk(chunk, false); 0776 } 0777 0778 static float calculateTextAnchorShiftForTextChunk(SVGTextChunk &chunk, ETextAnchor anchor) 0779 { 0780 float shift = 0.0f; 0781 0782 if (chunk.isVerticalText) { 0783 shift = cummulatedHeightOfTextChunk(chunk); 0784 } else { 0785 shift = cummulatedWidthOfTextChunk(chunk); 0786 } 0787 0788 // qCDebug(KHTML_LOG) << anchor << shift << TA_MIDDLE; 0789 0790 if (anchor == TA_MIDDLE) { 0791 shift *= -0.5f; 0792 } else { 0793 shift *= -1.0f; 0794 } 0795 0796 return shift; 0797 } 0798 0799 static void applyTextAnchorToTextChunk(SVGTextChunk &chunk) 0800 { 0801 // This method is not called for chunks containing chars aligned on a path. 0802 // -> all characters are visible, no need to check for "isHidden()" anywhere. 0803 0804 if (chunk.anchor == TA_START) { 0805 return; 0806 } 0807 0808 float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor); 0809 0810 // Apply correction to chunk 0811 Vector<SVGChar>::iterator chunkIt = chunk.start; 0812 for (; chunkIt != chunk.end; ++chunkIt) { 0813 SVGChar &curChar = *chunkIt; 0814 0815 if (chunk.isVerticalText) { 0816 curChar.y += shift; 0817 } else { 0818 curChar.x += shift; 0819 } 0820 } 0821 0822 // Move inline boxes 0823 Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); 0824 Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); 0825 0826 for (; boxIt != boxEnd; ++boxIt) { 0827 SVGInlineBoxCharacterRange &range = *boxIt; 0828 0829 InlineBox *curBox = range.box; 0830 ASSERT(curBox->isInlineTextBox()); 0831 ASSERT(curBox->parent() && (curBox->parent()->isRootInlineBox() || curBox->parent()->isInlineFlowBox())); 0832 0833 // Move target box 0834 if (chunk.isVerticalText) { 0835 curBox->setYPos(curBox->yPos() + static_cast<int>(shift)); 0836 } else { 0837 curBox->setXPos(curBox->xPos() + static_cast<int>(shift)); 0838 } 0839 } 0840 } 0841 0842 static float calculateTextLengthCorrectionForTextChunk(SVGTextChunk &chunk, ELengthAdjust lengthAdjust, float &computedLength) 0843 { 0844 //qCDebug(KHTML_LOG) << "text length"; 0845 if (chunk.textLength <= 0.0f) { 0846 return 0.0f; 0847 } 0848 0849 float computedWidth = cummulatedWidthOfTextChunk(chunk); 0850 float computedHeight = cummulatedHeightOfTextChunk(chunk); 0851 0852 if ((computedWidth <= 0.0f && !chunk.isVerticalText) || 0853 (computedHeight <= 0.0f && chunk.isVerticalText)) { 0854 return 0.0f; 0855 } 0856 0857 if (chunk.isVerticalText) { 0858 computedLength = computedHeight; 0859 } else { 0860 computedLength = computedWidth; 0861 } 0862 0863 if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { 0864 if (chunk.isVerticalText) { 0865 chunk.ctm.scale(1.0f, chunk.textLength / computedLength); 0866 } else { 0867 chunk.ctm.scale(chunk.textLength / computedLength, 1.0f); 0868 } 0869 0870 return 0.0f; 0871 } 0872 0873 return (chunk.textLength - computedLength) / float(chunk.end - chunk.start); 0874 } 0875 0876 static void applyTextLengthCorrectionToTextChunk(SVGTextChunk &chunk) 0877 { 0878 // This method is not called for chunks containing chars aligned on a path. 0879 // -> all characters are visible, no need to check for "isHidden()" anywhere. 0880 0881 // lengthAdjust="spacingAndGlyphs" is handled by modifying chunk.ctm 0882 float computedLength = 0.0f; 0883 float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, computedLength); 0884 0885 if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { 0886 SVGChar &firstChar = *(chunk.start); 0887 0888 // Assure we apply the chunk scaling in the right origin 0889 AffineTransform newChunkCtm; 0890 newChunkCtm.translate(firstChar.x, firstChar.y); 0891 newChunkCtm = chunk.ctm * newChunkCtm; 0892 newChunkCtm.translate(-firstChar.x, -firstChar.y); 0893 0894 chunk.ctm = newChunkCtm; 0895 } 0896 0897 // Apply correction to chunk 0898 if (spacingToApply != 0.0f) { 0899 Vector<SVGChar>::iterator chunkIt = chunk.start; 0900 for (; chunkIt != chunk.end; ++chunkIt) { 0901 SVGChar &curChar = *chunkIt; 0902 curChar.drawnSeperated = true; 0903 0904 if (chunk.isVerticalText) { 0905 curChar.y += (chunkIt - chunk.start) * spacingToApply; 0906 } else { 0907 curChar.x += (chunkIt - chunk.start) * spacingToApply; 0908 } 0909 } 0910 } 0911 } 0912 0913 void SVGRootInlineBox::computePerCharacterLayoutInformation() 0914 { 0915 //qCDebug(KHTML_LOG) << "computePerCharacterLayoutInformation()"; 0916 // Clean up any previous layout information 0917 m_svgChars.clear(); 0918 m_svgTextChunks.clear(); 0919 0920 // Build layout information for all contained render objects 0921 SVGCharacterLayoutInfo info(m_svgChars); 0922 //qCDebug(KHTML_LOG) << "before build layout info"; 0923 buildLayoutInformation(this, info); 0924 //qCDebug(KHTML_LOG) << "after build layout info"; 0925 0926 // Now all layout information are available for every character 0927 // contained in any of our child inline/flow boxes. Build list 0928 // of text chunks now, to be able to apply text-anchor shifts. 0929 buildTextChunks(m_svgChars, m_svgTextChunks, this); 0930 //qCDebug(KHTML_LOG) << "after build text chunks"; 0931 0932 // Layout all text chunks 0933 // text-anchor needs to be applied to individual chunks. 0934 layoutTextChunks(); 0935 //qCDebug(KHTML_LOG) << "after layout text chunks"; 0936 0937 // Finally the top left position of our box is known. 0938 // Propagate this knownledge to our RenderSVGText parent. 0939 FloatPoint topLeft = topLeftPositionOfCharacterRange(m_svgChars); 0940 object()->setPos((int) floorf(topLeft.x()), (int) floorf(topLeft.y())); 0941 0942 // Layout all InlineText/Flow boxes 0943 // BEWARE: This requires the root top/left position to be set correctly before! 0944 //qCDebug(KHTML_LOG) << "before layout inline boxes"; 0945 layoutInlineBoxes(); 0946 //qCDebug(KHTML_LOG) << "at the end"; 0947 } 0948 0949 void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox *start, SVGCharacterLayoutInfo &info) 0950 { 0951 if (start->isRootInlineBox()) { 0952 ASSERT(start->object()->element()->hasTagName(SVGNames::textTag)); 0953 0954 SVGTextPositioningElement *positioningElement = static_cast<SVGTextPositioningElement *>(start->object()->element()); 0955 ASSERT(positioningElement); 0956 ASSERT(positioningElement->parentNode()); 0957 0958 info.addLayoutInformation(positioningElement); 0959 } 0960 0961 LastGlyphInfo lastGlyph; 0962 0963 for (InlineBox *curr = start->firstChild(); curr; curr = curr->nextOnLine()) { 0964 if (curr->object()->isText()) { 0965 buildLayoutInformationForTextBox(info, static_cast<InlineTextBox *>(curr), lastGlyph); 0966 } else { 0967 ASSERT(curr->isInlineFlowBox()); 0968 InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(curr); 0969 0970 bool isAnchor = flowBox->object()->element()->hasTagName(SVGNames::aTag); 0971 bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag); 0972 0973 if (!isTextPath && !isAnchor) { 0974 SVGTextPositioningElement *positioningElement = static_cast<SVGTextPositioningElement *>(flowBox->object()->element()); 0975 ASSERT(positioningElement); 0976 ASSERT(positioningElement->parentNode()); 0977 0978 info.addLayoutInformation(positioningElement); 0979 } else if (!isAnchor) { 0980 info.setInPathLayout(true); 0981 0982 // Handle text-anchor/textLength on path, which is special. 0983 SVGTextContentElement *textContent = nullptr; 0984 Node *node = flowBox->object()->element(); 0985 if (node && node->isSVGElement()) { 0986 textContent = static_cast<SVGTextContentElement *>(node); 0987 } 0988 ASSERT(textContent); 0989 0990 ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); 0991 ETextAnchor anchor = flowBox->object()->style()->svgStyle()->textAnchor(); 0992 float textAnchorStartOffset = 0.0f; 0993 0994 // Initialize sub-layout. We need to create text chunks from the textPath 0995 // children using our standard layout code, to be able to measure the 0996 // text length using our normal methods and not textPath specific hacks. 0997 Vector<SVGChar> tempChars; 0998 Vector<SVGTextChunk> tempChunks; 0999 1000 SVGCharacterLayoutInfo tempInfo(tempChars); 1001 buildLayoutInformation(flowBox, tempInfo); 1002 1003 buildTextChunks(tempChars, tempChunks, flowBox); 1004 1005 Vector<SVGTextChunk>::iterator it = tempChunks.begin(); 1006 Vector<SVGTextChunk>::iterator end = tempChunks.end(); 1007 1008 AffineTransform ctm; 1009 float computedLength = 0.0f; 1010 1011 for (; it != end; ++it) { 1012 SVGTextChunk &chunk = *it; 1013 1014 // Apply text-length calculation 1015 info.pathExtraAdvance += calculateTextLengthCorrectionForTextChunk(chunk, lengthAdjust, computedLength); 1016 1017 if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { 1018 info.pathTextLength += computedLength; 1019 info.pathChunkLength += chunk.textLength; 1020 } 1021 1022 // Calculate text-anchor start offset 1023 if (anchor == TA_START) { 1024 continue; 1025 } 1026 1027 textAnchorStartOffset += calculateTextAnchorShiftForTextChunk(chunk, anchor); 1028 } 1029 1030 info.addLayoutInformation(flowBox, textAnchorStartOffset); 1031 } 1032 1033 float shiftxSaved = info.shiftx; 1034 float shiftySaved = info.shifty; 1035 1036 buildLayoutInformation(flowBox, info); 1037 info.processedChunk(shiftxSaved, shiftySaved); 1038 1039 if (isTextPath) { 1040 info.setInPathLayout(false); 1041 } 1042 } 1043 } 1044 } 1045 1046 void SVGRootInlineBox::layoutInlineBoxes() 1047 { 1048 int lowX = INT_MAX; 1049 int lowY = INT_MAX; 1050 int highX = INT_MIN; 1051 int highY = INT_MIN; 1052 1053 // Layout all child boxes 1054 Vector<SVGChar>::iterator it = m_svgChars.begin(); 1055 layoutInlineBoxes(this, it, lowX, highX, lowY, highY); 1056 ASSERT(it == m_svgChars.end()); 1057 } 1058 1059 void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox *start, Vector<SVGChar>::iterator &it, int &lowX, int &highX, int &lowY, int &highY) 1060 { 1061 for (InlineBox *curr = start->firstChild(); curr; curr = curr->nextOnLine()) { 1062 RenderStyle *style = curr->object()->style(); 1063 const Font &font = style->htmlFont(); 1064 1065 if (curr->object()->isText()) { 1066 SVGInlineTextBox *textBox = static_cast<SVGInlineTextBox *>(curr); 1067 unsigned length = textBox->len(); 1068 1069 SVGChar curChar = *it; 1070 ASSERT(it != m_svgChars.end()); 1071 1072 FloatRect stringRect; 1073 for (unsigned i = 0; i < length; ++i) { 1074 ASSERT(it != m_svgChars.end()); 1075 1076 if (it->isHidden()) { 1077 ++it; 1078 continue; 1079 } 1080 1081 stringRect.unite(textBox->calculateGlyphBoundaries(style, textBox->start() + i, *it)); 1082 ++it; 1083 } 1084 1085 IntRect enclosedStringRect = enclosingIntRect(stringRect); 1086 1087 int minX = enclosedStringRect.x(); 1088 int maxX = minX + enclosedStringRect.width(); 1089 1090 int minY = enclosedStringRect.y(); 1091 int maxY = minY + enclosedStringRect.height(); 1092 1093 curr->setXPos(minX - object()->xPos()); 1094 curr->setWidth(enclosedStringRect.width()); 1095 1096 curr->setYPos(minY - object()->yPos()); 1097 curr->setBaseline(font.ascent()); 1098 curr->setHeight(enclosedStringRect.height()); 1099 1100 if (minX < lowX) { 1101 lowX = minX; 1102 } 1103 1104 if (maxX > highX) { 1105 highX = maxX; 1106 } 1107 1108 if (minY < lowY) { 1109 lowY = minY; 1110 } 1111 1112 if (maxY > highY) { 1113 highY = maxY; 1114 } 1115 } else { 1116 ASSERT(curr->isInlineFlowBox()); 1117 1118 int minX = INT_MAX; 1119 int minY = INT_MAX; 1120 int maxX = INT_MIN; 1121 int maxY = INT_MIN; 1122 1123 InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(curr); 1124 layoutInlineBoxes(flowBox, it, minX, maxX, minY, maxY); 1125 1126 curr->setXPos(minX - object()->xPos()); 1127 curr->setWidth(maxX - minX); 1128 1129 curr->setYPos(minY - object()->yPos()); 1130 curr->setBaseline(font.ascent()); 1131 curr->setHeight(maxY - minY); 1132 1133 if (minX < lowX) { 1134 lowX = minX; 1135 } 1136 1137 if (maxX > highX) { 1138 highX = maxX; 1139 } 1140 1141 if (minY < lowY) { 1142 lowY = minY; 1143 } 1144 1145 if (maxY > highY) { 1146 highY = maxY; 1147 } 1148 } 1149 } 1150 1151 if (start->isRootInlineBox()) { 1152 int top = lowY - object()->yPos(); 1153 //int bottom = highY - object()->yPos(); 1154 1155 start->setXPos(lowX - object()->xPos()); 1156 start->setYPos(top); 1157 1158 start->setWidth(highX - lowX); 1159 start->setHeight(highY - lowY); 1160 1161 /*FIXME start->setVerticalOverflowPositions(top, bottom); 1162 start->setVerticalSelectionPositions(top, bottom);*/ 1163 } 1164 } 1165 1166 void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo &info, InlineTextBox *textBox, LastGlyphInfo &lastGlyph) 1167 { 1168 Q_UNUSED(lastGlyph); 1169 1170 RenderText *text = textBox->renderText(); 1171 ASSERT(text); 1172 1173 RenderStyle *style = text->style(/*textBox->isFirstLineStyle()*/); 1174 ASSERT(style); 1175 1176 const Font &font = style->htmlFont(); 1177 SVGInlineTextBox *svgTextBox = static_cast<SVGInlineTextBox *>(textBox); 1178 1179 unsigned length = textBox->len(); 1180 1181 const SVGRenderStyle *svgStyle = style->svgStyle(); 1182 bool isVerticalText = isVerticalWritingMode(svgStyle); 1183 1184 int charsConsumed = 0; 1185 for (unsigned i = 0; i < length; i += charsConsumed) { 1186 SVGChar svgChar; 1187 1188 if (info.inPathLayout()) { 1189 svgChar.pathData = SVGCharOnPath::create(); 1190 } 1191 1192 float glyphWidth = 0.0f; 1193 float glyphHeight = 0.0f; 1194 1195 int extraCharsAvailable = length - i - 1; 1196 1197 String unicodeStr; 1198 String glyphName; 1199 if (textBox->m_reversed) { 1200 glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName); 1201 glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable); 1202 unicodeStr = String(textBox->renderText()->text()/*->characters()*/ + textBox->end() - i, charsConsumed); 1203 } else { 1204 glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed, glyphName); 1205 glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->start() + i, extraCharsAvailable); 1206 unicodeStr = String(textBox->renderText()->text()/*->characters()*/ + textBox->start() + i, charsConsumed); 1207 } 1208 1209 bool assignedX = false; 1210 bool assignedY = false; 1211 1212 if (info.xValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && !isVerticalText))) { 1213 if (!isVerticalText) { 1214 svgChar.newTextChunk = true; 1215 } 1216 1217 assignedX = true; 1218 svgChar.drawnSeperated = true; 1219 info.curx = info.xValueNext(); 1220 } 1221 1222 if (info.yValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && isVerticalText))) { 1223 if (isVerticalText) { 1224 svgChar.newTextChunk = true; 1225 } 1226 1227 assignedY = true; 1228 svgChar.drawnSeperated = true; 1229 info.cury = info.yValueNext(); 1230 } 1231 1232 float dx = 0.0f; 1233 float dy = 0.0f; 1234 1235 // Apply x-axis shift 1236 if (info.dxValueAvailable()) { 1237 svgChar.drawnSeperated = true; 1238 1239 dx = info.dxValueNext(); 1240 info.dx += dx; 1241 1242 if (!info.inPathLayout()) { 1243 info.curx += dx; 1244 } 1245 } 1246 1247 // Apply y-axis shift 1248 if (info.dyValueAvailable()) { 1249 svgChar.drawnSeperated = true; 1250 1251 dy = info.dyValueNext(); 1252 info.dy += dy; 1253 1254 if (!info.inPathLayout()) { 1255 info.cury += dy; 1256 } 1257 } 1258 1259 // Take letter & word spacing and kerning into account 1260 float spacing = 0;//FIXME font.letterSpacing() + calculateKerning(textBox->object()->element()->renderer()); 1261 1262 const UChar *currentCharacter = text->text()/*->characters()*/ + (textBox->m_reversed ? textBox->end() - i : textBox->start() + i); 1263 #if 0 // FIXME (see below) 1264 const UChar *lastCharacter = 0; 1265 1266 if (textBox->m_reversed) { 1267 if (i < textBox->end()) { 1268 lastCharacter = text->text()/*characters()*/ + textBox->end() - i + 1; 1269 } 1270 } else { 1271 if (i > 0) { 1272 lastCharacter = text->text()/*characters()*/ + textBox->start() + i - 1; 1273 } 1274 } 1275 #endif 1276 1277 if (info.nextDrawnSeperated || spacing != 0.0f) { 1278 info.nextDrawnSeperated = false; 1279 svgChar.drawnSeperated = true; 1280 } 1281 1282 /*FIXME if (currentCharacter && Font::treatAsSpace(*currentCharacter) && lastCharacter && !Font::treatAsSpace(*lastCharacter)) { 1283 spacing += font.wordSpacing(); 1284 1285 if (spacing != 0.0f && !info.inPathLayout()) 1286 info.nextDrawnSeperated = true; 1287 }*/ 1288 1289 float orientationAngle = glyphOrientationToAngle(svgStyle, isVerticalText, *currentCharacter); 1290 1291 float xOrientationShift = 0.0f; 1292 float yOrientationShift = 0.0f; 1293 float glyphAdvance = calculateGlyphAdvanceAndShiftRespectingOrientation(isVerticalText, orientationAngle, glyphWidth, glyphHeight, 1294 font, svgChar, xOrientationShift, yOrientationShift); 1295 1296 // Handle textPath layout mode 1297 if (info.inPathLayout()) { 1298 float extraAdvance = isVerticalText ? dy : dx; 1299 float newOffset = FLT_MIN; 1300 1301 if (assignedX && !isVerticalText) { 1302 newOffset = info.curx; 1303 } else if (assignedY && isVerticalText) { 1304 newOffset = info.cury; 1305 } 1306 1307 float correctedGlyphAdvance = glyphAdvance; 1308 1309 // Handle lengthAdjust="spacingAndGlyphs" by specifying per-character scale operations 1310 if (info.pathTextLength > 0.0f && info.pathChunkLength > 0.0f) { 1311 if (isVerticalText) { 1312 svgChar.pathData->yScale = info.pathChunkLength / info.pathTextLength; 1313 spacing *= svgChar.pathData->yScale; 1314 correctedGlyphAdvance *= svgChar.pathData->yScale; 1315 } else { 1316 svgChar.pathData->xScale = info.pathChunkLength / info.pathTextLength; 1317 spacing *= svgChar.pathData->xScale; 1318 correctedGlyphAdvance *= svgChar.pathData->xScale; 1319 } 1320 } 1321 1322 // Handle letter & word spacing on text path 1323 float pathExtraAdvance = info.pathExtraAdvance; 1324 info.pathExtraAdvance += spacing; 1325 1326 svgChar.pathData->hidden = !info.nextPathLayoutPointAndAngle(correctedGlyphAdvance, extraAdvance, newOffset); 1327 svgChar.drawnSeperated = true; 1328 1329 info.pathExtraAdvance = pathExtraAdvance; 1330 } 1331 1332 // Apply rotation 1333 if (info.angleValueAvailable()) { 1334 info.angle = info.angleValueNext(); 1335 } 1336 1337 // Apply baseline-shift 1338 if (info.baselineShiftValueAvailable()) { 1339 svgChar.drawnSeperated = true; 1340 float shift = info.baselineShiftValueNext(); 1341 1342 if (isVerticalText) { 1343 info.shiftx += shift; 1344 } else { 1345 info.shifty -= shift; 1346 } 1347 } 1348 1349 // Take dominant-baseline / alignment-baseline into account 1350 yOrientationShift += alignmentBaselineToShift(isVerticalText, text, font); 1351 1352 svgChar.x = info.curx; 1353 svgChar.y = info.cury; 1354 svgChar.angle = info.angle; 1355 1356 // For text paths any shift (dx/dy/baseline-shift) has to be applied after the rotation 1357 if (!info.inPathLayout()) { 1358 svgChar.x += info.shiftx + xOrientationShift; 1359 svgChar.y += info.shifty + yOrientationShift; 1360 1361 if (orientationAngle != 0.0f) { 1362 svgChar.angle += orientationAngle; 1363 } 1364 1365 if (svgChar.angle != 0.0f) { 1366 svgChar.drawnSeperated = true; 1367 } 1368 } else { 1369 svgChar.pathData->orientationAngle = orientationAngle; 1370 1371 if (isVerticalText) { 1372 svgChar.angle -= 90.0f; 1373 } 1374 1375 svgChar.pathData->xShift = info.shiftx + xOrientationShift; 1376 svgChar.pathData->yShift = info.shifty + yOrientationShift; 1377 1378 // Translate to glyph midpoint 1379 if (isVerticalText) { 1380 svgChar.pathData->xShift += info.dx; 1381 svgChar.pathData->yShift -= glyphAdvance / 2.0f; 1382 } else { 1383 svgChar.pathData->xShift -= glyphAdvance / 2.0f; 1384 svgChar.pathData->yShift += info.dy; 1385 } 1386 } 1387 1388 double kerning = 0.0; 1389 #if ENABLE(SVG_FONTS) 1390 /*FIXME khtml SVGFontElement* svgFont = 0; 1391 if (style->font().isSVGFont()) 1392 svgFont = style->font().svgFont(); 1393 1394 if (lastGlyph.isValid && style->font().isSVGFont()) { 1395 SVGHorizontalKerningPair kerningPair; 1396 if (svgFont->getHorizontalKerningPairForStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeStr, glyphName, kerningPair)) 1397 kerning = kerningPair.kerning; 1398 } 1399 1400 if (style->font().isSVGFont()) { 1401 lastGlyph.unicode = unicodeStr; 1402 lastGlyph.glyphName = glyphName; 1403 lastGlyph.isValid = true; 1404 } else 1405 lastGlyph.isValid = false;*/ 1406 #endif 1407 1408 svgChar.x -= (float)kerning; 1409 1410 // Advance to new position 1411 if (isVerticalText) { 1412 svgChar.drawnSeperated = true; 1413 info.cury += glyphAdvance + spacing; 1414 } else { 1415 info.curx += glyphAdvance + spacing - (float)kerning; 1416 } 1417 1418 // Advance to next character group 1419 for (int k = 0; k < charsConsumed; ++k) { 1420 info.svgChars.append(svgChar); 1421 info.processedSingleCharacter(); 1422 svgChar.drawnSeperated = false; 1423 svgChar.newTextChunk = false; 1424 } 1425 } 1426 } 1427 1428 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar> &svgChars, Vector<SVGTextChunk> &svgTextChunks, InlineFlowBox *start) 1429 { 1430 SVGTextChunkLayoutInfo info(svgTextChunks); 1431 info.it = svgChars.begin(); 1432 info.chunk.start = svgChars.begin(); 1433 info.chunk.end = svgChars.begin(); 1434 1435 buildTextChunks(svgChars, start, info); 1436 ASSERT(info.it == svgChars.end()); 1437 } 1438 1439 void SVGRootInlineBox::buildTextChunks(Vector<SVGChar> &svgChars, InlineFlowBox *start, SVGTextChunkLayoutInfo &info) 1440 { 1441 #if DEBUG_CHUNK_BUILDING > 1 1442 fprintf(stderr, " -> buildTextChunks(start=%p)\n", start); 1443 #endif 1444 1445 for (InlineBox *curr = start->firstChild(); curr; curr = curr->nextOnLine()) { 1446 if (curr->object()->isText()) { 1447 InlineTextBox *textBox = static_cast<InlineTextBox *>(curr); 1448 1449 unsigned length = textBox->len(); 1450 if (!length) { 1451 continue; 1452 } 1453 1454 #if DEBUG_CHUNK_BUILDING > 1 1455 fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n", 1456 textBox, length, textBox->start(), textBox->end(), (int) info.handlingTextPath); 1457 #endif 1458 1459 RenderText *text = textBox->renderText(); 1460 ASSERT(text); 1461 ASSERT(text->element()); 1462 1463 SVGTextContentElement *textContent = nullptr; 1464 Node *node = text->element()->parent(); 1465 if (node && node->isSVGElement()) { 1466 textContent = static_cast<SVGTextContentElement *>(node); 1467 } 1468 ASSERT(textContent); 1469 1470 // Start new character range for the first chunk 1471 bool isFirstCharacter = info.svgTextChunks.isEmpty() && info.chunk.start == info.it && info.chunk.start == info.chunk.end; 1472 if (isFirstCharacter) { 1473 ASSERT(info.chunk.boxes.isEmpty()); 1474 info.chunk.boxes.append(SVGInlineBoxCharacterRange()); 1475 } else { 1476 ASSERT(!info.chunk.boxes.isEmpty()); 1477 } 1478 1479 // Walk string to find out new chunk positions, if existent 1480 for (unsigned i = 0; i < length; ++i) { 1481 ASSERT(info.it != svgChars.end()); 1482 1483 SVGInlineBoxCharacterRange &range = info.chunk.boxes.last(); 1484 if (range.isOpen()) { 1485 range.box = curr; 1486 range.startOffset = (i == 0 ? 0 : i - 1); 1487 1488 #if DEBUG_CHUNK_BUILDING > 1 1489 fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset); 1490 #endif 1491 } 1492 1493 // If a new (or the first) chunk has been started, record it's text-anchor and writing mode. 1494 if (info.assignChunkProperties) { 1495 info.assignChunkProperties = false; 1496 1497 info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle()); 1498 info.chunk.isTextPath = info.handlingTextPath; 1499 info.chunk.anchor = text->style()->svgStyle()->textAnchor(); 1500 info.chunk.textLength = textContent->textLength().value(); 1501 info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); 1502 1503 #if DEBUG_CHUNK_BUILDING > 1 1504 fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", info.chunk.isVerticalText, info.chunk.anchor); 1505 #endif 1506 } 1507 1508 if (i > 0 && !isFirstCharacter && (*info.it).newTextChunk) { 1509 // Close mid chunk & character range 1510 ASSERT(!range.isOpen()); 1511 ASSERT(!range.isClosed()); 1512 1513 range.endOffset = i; 1514 closeTextChunk(info); 1515 1516 #if DEBUG_CHUNK_BUILDING > 1 1517 fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset); 1518 #endif 1519 1520 // Prepare for next chunk, if we're not at the end 1521 startTextChunk(info); 1522 if (i + 1 == length) { 1523 #if DEBUG_CHUNK_BUILDING > 1 1524 fprintf(stderr, " | -> Record last chunk of inline text box!\n"); 1525 #endif 1526 1527 startTextChunk(info); 1528 SVGInlineBoxCharacterRange &range = info.chunk.boxes.last(); 1529 1530 info.assignChunkProperties = false; 1531 info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle()); 1532 info.chunk.isTextPath = info.handlingTextPath; 1533 info.chunk.anchor = text->style()->svgStyle()->textAnchor(); 1534 info.chunk.textLength = textContent->textLength().value(); 1535 info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); 1536 1537 range.box = curr; 1538 range.startOffset = i; 1539 1540 ASSERT(!range.isOpen()); 1541 ASSERT(!range.isClosed()); 1542 } 1543 } 1544 1545 // This should only hold true for the first character of the first chunk 1546 if (isFirstCharacter) { 1547 isFirstCharacter = false; 1548 } 1549 1550 ++info.it; 1551 } 1552 1553 #if DEBUG_CHUNK_BUILDING > 1 1554 fprintf(stderr, " -> Finished inline text box!\n"); 1555 #endif 1556 1557 SVGInlineBoxCharacterRange &range = info.chunk.boxes.last(); 1558 if (!range.isOpen() && !range.isClosed()) { 1559 #if DEBUG_CHUNK_BUILDING > 1 1560 fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length); 1561 #endif 1562 1563 // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk. 1564 range.endOffset = length; 1565 1566 if (info.it != svgChars.end()) { 1567 #if DEBUG_CHUNK_BUILDING > 1 1568 fprintf(stderr, " -> Not at last character yet!\n"); 1569 #endif 1570 1571 // If we're not at the end of the last box to be processed, and if the next 1572 // character starts a new chunk, then close the current chunk and start a new one. 1573 if ((*info.it).newTextChunk) { 1574 #if DEBUG_CHUNK_BUILDING > 1 1575 fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n"); 1576 #endif 1577 1578 closeTextChunk(info); 1579 startTextChunk(info); 1580 } else { 1581 // Just start a new character range 1582 info.chunk.boxes.append(SVGInlineBoxCharacterRange()); 1583 1584 #if DEBUG_CHUNK_BUILDING > 1 1585 fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n"); 1586 #endif 1587 } 1588 } else { 1589 #if DEBUG_CHUNK_BUILDING > 1 1590 fprintf(stderr, " -> Closing final chunk! Finished processing!\n"); 1591 #endif 1592 1593 // Close final chunk, once we're at the end of the last box 1594 closeTextChunk(info); 1595 } 1596 } 1597 } else { 1598 ASSERT(curr->isInlineFlowBox()); 1599 InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(curr); 1600 1601 bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag); 1602 1603 #if DEBUG_CHUNK_BUILDING > 1 1604 fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath); 1605 #endif 1606 1607 if (isTextPath) { 1608 info.handlingTextPath = true; 1609 } 1610 1611 buildTextChunks(svgChars, flowBox, info); 1612 1613 if (isTextPath) { 1614 info.handlingTextPath = false; 1615 } 1616 } 1617 } 1618 1619 #if DEBUG_CHUNK_BUILDING > 1 1620 fprintf(stderr, " <- buildTextChunks(start=%p)\n", start); 1621 #endif 1622 } 1623 1624 const Vector<SVGTextChunk> &SVGRootInlineBox::svgTextChunks() const 1625 { 1626 return m_svgTextChunks; 1627 } 1628 1629 void SVGRootInlineBox::layoutTextChunks() 1630 { 1631 Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin(); 1632 Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end(); 1633 1634 for (; it != end; ++it) { 1635 SVGTextChunk &chunk = *it; 1636 1637 #if DEBUG_CHUNK_BUILDING > 0 1638 { 1639 fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n", 1640 (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText, 1641 (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int)(chunk.end - chunk.start)); 1642 1643 Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); 1644 Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); 1645 1646 unsigned int i = 0; 1647 for (; boxIt != boxEnd; ++boxIt) { 1648 SVGInlineBoxCharacterRange &range = *boxIt; i++; 1649 fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box); 1650 } 1651 } 1652 #endif 1653 1654 if (chunk.isTextPath) { 1655 continue; 1656 } 1657 1658 // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts. 1659 applyTextLengthCorrectionToTextChunk(chunk); 1660 1661 // text-anchor is already handled for textPath layouts. 1662 applyTextAnchorToTextChunk(chunk); 1663 } 1664 } 1665 1666 static inline void addPaintServerToTextDecorationInfo(ETextDecoration decoration, SVGTextDecorationInfo &info, RenderObject *object) 1667 { 1668 if (object->style()->svgStyle()->hasFill()) { 1669 info.fillServerMap.set(decoration, object); 1670 } 1671 1672 if (object->style()->svgStyle()->hasStroke()) { 1673 info.strokeServerMap.set(decoration, object); 1674 } 1675 } 1676 1677 SVGTextDecorationInfo SVGRootInlineBox::retrievePaintServersForTextDecoration(RenderObject *start) 1678 { 1679 ASSERT(start); 1680 1681 Vector<RenderObject *> parentChain; 1682 while ((start = start->parent())) { 1683 parentChain.prepend(start); 1684 1685 // Stop at our direct <text> parent. 1686 if (start->isSVGText()) { 1687 break; 1688 } 1689 } 1690 1691 Vector<RenderObject *>::iterator it = parentChain.begin(); 1692 Vector<RenderObject *>::iterator end = parentChain.end(); 1693 1694 SVGTextDecorationInfo info; 1695 1696 for (; it != end; ++it) { 1697 RenderObject *object = *it; 1698 ASSERT(object); 1699 1700 RenderStyle *style = object->style(); 1701 ASSERT(style); 1702 1703 int decorations = style->textDecoration(); 1704 if (decorations != NONE) { 1705 if (decorations & OVERLINE) { 1706 addPaintServerToTextDecorationInfo(OVERLINE, info, object); 1707 } 1708 1709 if (decorations & UNDERLINE) { 1710 addPaintServerToTextDecorationInfo(UNDERLINE, info, object); 1711 } 1712 1713 if (decorations & LINE_THROUGH) { 1714 addPaintServerToTextDecorationInfo(LINE_THROUGH, info, object); 1715 } 1716 } 1717 } 1718 1719 return info; 1720 } 1721 1722 void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase *walker, const SVGInlineTextBox *textBox) 1723 { 1724 ASSERT(walker); 1725 1726 Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin(); 1727 Vector<SVGTextChunk>::iterator itEnd = m_svgTextChunks.end(); 1728 1729 for (; it != itEnd; ++it) { 1730 SVGTextChunk &curChunk = *it; 1731 1732 Vector<SVGInlineBoxCharacterRange>::iterator boxIt = curChunk.boxes.begin(); 1733 Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = curChunk.boxes.end(); 1734 1735 InlineBox *lastNotifiedBox = nullptr; 1736 InlineBox *prevBox = nullptr; 1737 1738 unsigned int chunkOffset = 0; 1739 bool startedFirstChunk = false; 1740 1741 for (; boxIt != boxEnd; ++boxIt) { 1742 SVGInlineBoxCharacterRange &range = *boxIt; 1743 1744 ASSERT(range.box->isInlineTextBox()); 1745 SVGInlineTextBox *rangeTextBox = static_cast<SVGInlineTextBox *>(range.box); 1746 1747 if (textBox && rangeTextBox != textBox) { 1748 chunkOffset += range.endOffset - range.startOffset; 1749 continue; 1750 } 1751 1752 // Eventually notify that we started a new chunk 1753 if (!textBox && !startedFirstChunk) { 1754 startedFirstChunk = true; 1755 1756 lastNotifiedBox = range.box; 1757 walker->start(range.box); 1758 } else { 1759 // Eventually apply new style, as this chunk spans multiple boxes (with possible different styling) 1760 if (prevBox && prevBox != range.box) { 1761 lastNotifiedBox = range.box; 1762 1763 walker->end(prevBox); 1764 walker->start(lastNotifiedBox); 1765 } 1766 } 1767 1768 unsigned int length = range.endOffset - range.startOffset; 1769 1770 Vector<SVGChar>::iterator itCharBegin = curChunk.start + chunkOffset; 1771 Vector<SVGChar>::iterator itCharEnd = curChunk.start + chunkOffset + length; 1772 ASSERT(itCharEnd <= curChunk.end); 1773 1774 // Process this chunk portion 1775 if (textBox) { 1776 (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); 1777 } else { 1778 if (walker->setupFill(range.box)) { 1779 (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); 1780 } 1781 1782 if (walker->setupStroke(range.box)) { 1783 (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); 1784 } 1785 } 1786 1787 chunkOffset += length; 1788 1789 if (!textBox) { 1790 prevBox = range.box; 1791 } 1792 } 1793 1794 if (!textBox && startedFirstChunk) { 1795 walker->end(lastNotifiedBox); 1796 } 1797 } 1798 } 1799 1800 } // namespace WebCore 1801 1802 #endif // ENABLE(SVG)