📄 svgrootinlinebox.cpp
字号:
// Finalize text rendering if (!flowBox->isRootInlineBox()) { finishRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_savedInfo.context); m_filter = 0; } // Restore context & repaint rect m_paintInfo.context->restore(); m_paintInfo.rect = m_savedInfo.rect; } bool chunkSetupFillCallback(InlineBox* box) { InlineFlowBox* flowBox = box->parent(); // Setup fill paint server RenderObject* object = flowBox->renderer(); ASSERT(object); ASSERT(!m_strokePaintServer); teardownFillPaintServer(); m_fillPaintServer = SVGPaintServer::fillPaintServer(object->style(), object); if (m_fillPaintServer) { m_fillPaintServer->setup(m_paintInfo.context, object, ApplyToFillTargetType, true); m_fillPaintServerObject = object; return true; } return false; } bool chunkSetupStrokeCallback(InlineBox* box) { InlineFlowBox* flowBox = box->parent(); // Setup stroke paint server RenderObject* object = flowBox->renderer(); ASSERT(object); // If we're both stroked & filled, teardown fill paint server before stroking. teardownFillPaintServer(); teardownStrokePaintServer(); m_strokePaintServer = SVGPaintServer::strokePaintServer(object->style(), object); if (m_strokePaintServer) { m_strokePaintServer->setup(m_paintInfo.context, object, ApplyToStrokeTargetType, true); m_strokePaintServerObject = object; return true; } return false; } void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) { RenderText* text = textBox->textRenderer(); ASSERT(text); RenderStyle* styleToUse = text->style(textBox->isFirstLineStyle()); ASSERT(styleToUse); startOffset += textBox->start(); int textDecorations = styleToUse->textDecorationsInEffect(); int textWidth = 0; IntPoint decorationOrigin; SVGTextDecorationInfo info; if (!chunkCtm.isIdentity()) m_paintInfo.context->concatCTM(chunkCtm); for (Vector<SVGChar>::iterator it = start; it != end; ++it) { if (it->isHidden()) continue; // Determine how many characters - starting from the current - can be drawn at once. Vector<SVGChar>::iterator itSearch = it + 1; while (itSearch != end) { if (itSearch->drawnSeperated || itSearch->isHidden()) break; itSearch++; } const UChar* stringStart = text->characters() + startOffset + (it - start); unsigned int stringLength = itSearch - it; // Paint decorations, that have to be drawn before the text gets drawn if (textDecorations != TDNONE && m_paintInfo.phase != PaintPhaseSelection) { textWidth = styleToUse->font().width(svgTextRunForInlineTextBox(stringStart, stringLength, styleToUse, textBox, (*it).x)); decorationOrigin = IntPoint((int) (*it).x, (int) (*it).y - styleToUse->font().ascent()); info = m_rootBox->retrievePaintServersForTextDecoration(text); } if (textDecorations & UNDERLINE && textWidth != 0.0f) textBox->paintDecoration(UNDERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); if (textDecorations & OVERLINE && textWidth != 0.0f) textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); // Paint text SVGPaintServer* activePaintServer = m_fillPaintServer; if (!activePaintServer) activePaintServer = m_strokePaintServer; ASSERT(activePaintServer); textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, activePaintServer); // Paint decorations, that have to be drawn afterwards if (textDecorations & LINE_THROUGH && textWidth != 0.0f) textBox->paintDecoration(LINE_THROUGH, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); // Skip processed characters it = itSearch - 1; } if (!chunkCtm.isIdentity()) m_paintInfo.context->concatCTM(chunkCtm.inverse()); }private: SVGRootInlineBox* m_rootBox; bool m_chunkStarted : 1; RenderObject::PaintInfo m_paintInfo; RenderObject::PaintInfo m_savedInfo; FloatRect m_boundingBox; SVGResourceFilter* m_filter; SVGResourceFilter* m_rootFilter; SVGPaintServer* m_fillPaintServer; SVGPaintServer* m_strokePaintServer; RenderObject* m_fillPaintServerObject; RenderObject* m_strokePaintServerObject; int m_tx; int m_ty;};void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty){ if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground) return; RenderObject::PaintInfo savedInfo(paintInfo); paintInfo.context->save(); SVGResourceFilter* filter = 0; FloatRect boundingBox(tx + x(), ty + y(), width(), height()); // Initialize text rendering paintInfo.context->concatCTM(renderer()->localTransform()); prepareToRenderSVGContent(renderer(), paintInfo, boundingBox, filter); paintInfo.context->concatCTM(renderer()->localTransform().inverse()); // Render text, chunk-by-chunk SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty); SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback, &SVGRootInlineBoxPaintWalker::chunkPortionCallback, &SVGRootInlineBoxPaintWalker::chunkStartCallback, &SVGRootInlineBoxPaintWalker::chunkEndCallback, &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback, &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback); walkTextChunks(&walker); // Finalize text rendering finishRenderSVGContent(renderer(), paintInfo, boundingBox, filter, savedInfo.context); paintInfo.context->restore();}int SVGRootInlineBox::placeBoxesHorizontally(int, int& leftPosition, int& rightPosition, bool&){ // Remove any offsets caused by RTL text layout leftPosition = 0; rightPosition = 0; return 0;}int SVGRootInlineBox::verticallyAlignBoxes(int){ // height is set by layoutInlineBoxes. return height();}float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range){ ASSERT(!range.isOpen()); ASSERT(range.isClosed()); ASSERT(range.box->isInlineTextBox()); InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box); RenderText* text = textBox->textRenderer(); RenderStyle* style = text->style(); return style->font().floatWidth(svgTextRunForInlineTextBox(text->characters() + textBox->start() + range.startOffset, range.endOffset - range.startOffset, style, textBox, 0));}float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range){ ASSERT(!range.isOpen()); ASSERT(range.isClosed()); ASSERT(range.box->isInlineTextBox()); InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box); RenderText* text = textBox->textRenderer(); const Font& font = text->style()->font(); return (range.endOffset - range.startOffset) * (font.ascent() + font.descent());}TextRun svgTextRunForInlineTextBox(const UChar* c, int len, RenderStyle* style, const InlineTextBox* textBox, float xPos){ ASSERT(textBox); ASSERT(style); TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->direction() == RTL, textBox->m_dirOverride || style->visuallyOrdered());#if ENABLE(SVG_FONTS) run.setReferencingRenderObject(textBox->textRenderer()->parent());#endif // We handle letter & word spacing ourselves run.disableSpacing(); return run;}static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWidthOnly){ float length = 0.0f; Vector<SVGChar>::iterator charIt = chunk.start; Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin(); Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end(); for (; it != end; ++it) { SVGInlineBoxCharacterRange& range = *it; SVGInlineTextBox* box = static_cast<SVGInlineTextBox*>(range.box); RenderStyle* style = box->renderer()->style(); for (int i = range.startOffset; i < range.endOffset; ++i) { ASSERT(charIt <= chunk.end); // Determine how many characters - starting from the current - can be measured at once. // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width // of a string is not the sum of the boundaries of all contained glyphs. Vector<SVGChar>::iterator itSearch = charIt + 1; Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i; while (itSearch != endSearch) { // No need to check for 'isHidden()' here as this function is not called for text paths. if (itSearch->drawnSeperated) break; itSearch++; } unsigned int positionOffset = itSearch - charIt; // Calculate width/height of subrange SVGInlineBoxCharacterRange subRange; subRange.box = range.box; subRange.startOffset = i; subRange.endOffset = i + positionOffset; if (calcWidthOnly) length += cummulatedWidthOfInlineBoxCharacterRange(subRange); else length += cummulatedHeightOfInlineBoxCharacterRange(subRange); // Calculate gap between the previous & current range // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account // so add "40" as width, and analogous for B & C, add "20" as width. if (itSearch > chunk.start && itSearch < chunk.end) { SVGChar& lastCharacter = *(itSearch - 1); SVGChar& currentCharacter = *itSearch; int offset = box->direction() == RTL ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1; // FIXME: does this need to change to handle multichar glyphs? int charsConsumed = 1; String glyphName; if (calcWidthOnly) { float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName); length += currentCharacter.x - lastCharacter.x - lastGlyphWidth; } else { float lastGlyphHeight = box->calculateGlyphHeight(style, offset, 0); length += currentCharacter.y - lastCharacter.y - lastGlyphHeight; } } // Advance processed characters i += positionOffset - 1; charIt = itSearch; } } ASSERT(charIt == chunk.end); return length;}static float cummulatedWidthOfTextChunk(SVGTextChunk& chunk){ return cummulatedWidthOrHeightOfTextChunk(chunk, true);}static float cummulatedHeightOfTextChunk(SVGTextChunk& chunk){ return cummulatedWidthOrHeightOfTextChunk(chunk, false);}static float calculateTextAnchorShiftForTextChunk(SVGTextChunk& chunk, ETextAnchor anchor){ float shift = 0.0f; if (chunk.isVerticalText) shift = cummulatedHeightOfTextChunk(chunk); else shift = cummulatedWidthOfTextChunk(chunk); if (anchor == TA_MIDDLE) shift *= -0.5f; else shift *= -1.0f; return shift;}static void applyTextAnchorToTextChunk(SVGTextChunk& chunk){ // This method is not called for chunks containing chars aligned on a path. // -> all characters are visible, no need to check for "isHidden()" anywhere. if (chunk.anchor == TA_START) return; float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor); // Apply correction to chunk Vector<SVGChar>::iterator chunkIt = chunk.start; for (; chunkIt != chunk.end; ++chunkIt) { SVGChar& curChar = *chunkIt; if (chunk.isVerticalText) curChar.y += shift; else curChar.x += shift; } // Move inline boxes Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); for (; boxIt != boxEnd; ++boxIt) { SVGInlineBoxCharacterRange& range = *boxIt; InlineBox* curBox = range.box; ASSERT(curBox->isInlineTextBox()); // Move target box if (chunk.isVerticalText) curBox->setY(curBox->y() + static_cast<int>(shift)); else curBox->setX(curBox->x() + static_cast<int>(shift)); }}static float calculateTextLengthCorrectionForTextChunk(SVGTextChunk& chunk, ELengthAdjust lengthAdjust, float& computedLength){ if (chunk.textLength <= 0.0f) return 0.0f; float computedWidth = cummulatedWidthOfTextChunk(chunk); float computedHeight = cummulatedHeightOfTextChunk(chunk); if ((computedWidth <= 0.0f && !chunk.isVerticalText) || (computedHeight <= 0.0f && chunk.isVerticalText)) return 0.0f; if (chunk.isVerticalText) computedLength = computedHeight; else computedLength = computedWidth; if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { if (chunk.isVerticalText) chunk.ctm.scaleNonUniform(1.0f, chunk.textLength / computedLength); else chunk.ctm.scaleNonUniform(chunk.textLength / computedLength, 1.0f); return 0.0f; } return (chunk.textLength - computedLength) / float(chunk.end - chunk.start);}static void applyTextLengthCorrectionToTextChunk(SVGTextChunk& chunk){ // This method is not called for chunks containing chars aligned on a path. // -> all characters are visible, no need to check for "isHidden()" anywhere. // lengthAdjust="spacingAndGlyphs" is handled by modifying chunk.ctm float computedLength = 0.0f; float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, computedLength); if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { SVGChar& firstChar = *(chunk.start); // Assure we apply the chunk scaling in the right origin TransformationMatrix newChunkCtm; newChunkCtm.translate(firstChar.x, firstChar.y); newChunkCtm = chunk.ctm * newChunkCtm; newChunkCtm.translate(-firstChar.x, -firstChar.y); chunk.ctm = newChunkCtm; } // Apply correction to chunk if (spacingToApply != 0.0f) { Vector<SVGChar>::iterator chunkIt = chunk.start; for (; chunkIt != chunk.end; ++chunkIt) { SVGChar& curChar = *chunkIt; curChar.drawnSeperated = true;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -