📄 svginlinetextbox.cpp
字号:
/** * This file is part of the DOM implementation for KDE. * * Copyright (C) 2007 Rob Buis <buis@kde.org> * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */#include "config.h"#if ENABLE(SVG)#include "SVGInlineTextBox.h"#include "Document.h"#include "Editor.h"#include "Frame.h"#include "GraphicsContext.h"#include "InlineFlowBox.h"#include "Range.h"#include "SVGPaintServer.h"#include "SVGRootInlineBox.h"#include "Text.h"#include <float.h>using std::max;namespace WebCore {SVGInlineTextBox::SVGInlineTextBox(RenderObject* obj) : InlineTextBox(obj) , m_height(0){}int SVGInlineTextBox::selectionTop(){ return m_y;} int SVGInlineTextBox::selectionHeight(){ return m_height;}SVGRootInlineBox* SVGInlineTextBox::svgRootInlineBox() const{ // Find associated root inline box InlineFlowBox* parentBox = parent(); while (parentBox && !parentBox->isRootInlineBox()) parentBox = parentBox->parent(); ASSERT(parentBox); ASSERT(parentBox->isRootInlineBox()); if (!parentBox->isSVGRootInlineBox()) return 0; return static_cast<SVGRootInlineBox*>(parentBox);}float SVGInlineTextBox::calculateGlyphWidth(RenderStyle* style, int offset, int extraCharsAvailable, int& charsConsumed, String& glyphName) const{ ASSERT(style); return style->font().floatWidth(svgTextRunForInlineTextBox(textRenderer()->text()->characters() + offset, 1, style, this, 0), extraCharsAvailable, charsConsumed, glyphName);}float SVGInlineTextBox::calculateGlyphHeight(RenderStyle* style, int, int) const{ // This is just a guess, and the only purpose of this function is to centralize this hack. // In real-life top-top-bottom scripts this won't be enough, I fear. return style->font().ascent() + style->font().descent();}FloatRect SVGInlineTextBox::calculateGlyphBoundaries(RenderStyle* style, int offset, const SVGChar& svgChar) const{ const Font& font = style->font(); // Take RTL text into account and pick right glyph width/height. float glyphWidth = 0.0f; // FIXME: account for multi-character glyphs int charsConsumed; String glyphName; if (direction() == LTR) glyphWidth = calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName); else glyphWidth = calculateGlyphWidth(style, start() + end() - offset, 0, charsConsumed, glyphName); float x1 = svgChar.x; float x2 = svgChar.x + glyphWidth; float y1 = svgChar.y - font.ascent(); float y2 = svgChar.y + font.descent(); FloatRect glyphRect(x1, y1, x2 - x1, y2 - y1); // Take per-character transformations into account TransformationMatrix ctm = svgChar.characterTransform(); if (!ctm.isIdentity()) glyphRect = ctm.mapRect(glyphRect); return glyphRect;}// Helper class for closestCharacterToPosition()struct SVGInlineTextBoxClosestCharacterToPositionWalker { SVGInlineTextBoxClosestCharacterToPositionWalker(int x, int y) : m_character(0) , m_distance(FLT_MAX) , m_x(x) , m_y(y) , m_offsetOfHitCharacter(0) { } void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) { RenderStyle* style = textBox->textRenderer()->style(); Vector<SVGChar>::iterator closestCharacter = 0; unsigned int closestOffset = UINT_MAX; for (Vector<SVGChar>::iterator it = start; it != end; ++it) { if (it->isHidden()) continue; unsigned int newOffset = textBox->start() + (it - start) + startOffset; FloatRect glyphRect = chunkCtm.mapRect(textBox->calculateGlyphBoundaries(style, newOffset, *it)); // Take RTL text into account and pick right glyph width/height. // NOTE: This offset has to be corrected _after_ calling calculateGlyphBoundaries if (textBox->direction() == RTL) newOffset = textBox->start() + textBox->end() - newOffset; // Calculate distances relative to the glyph mid-point. I hope this is accurate enough. float xDistance = glyphRect.x() + glyphRect.width() / 2.0f - m_x; float yDistance = glyphRect.y() - glyphRect.height() / 2.0f - m_y; float newDistance = sqrtf(xDistance * xDistance + yDistance * yDistance); if (newDistance <= m_distance) { m_distance = newDistance; closestOffset = newOffset; closestCharacter = it; } } if (closestOffset != UINT_MAX) { // Record current chunk, if it contains the current closest character next to the mouse. m_character = closestCharacter; m_offsetOfHitCharacter = closestOffset; } } SVGChar* character() const { return m_character; } int offsetOfHitCharacter() const { if (!m_character) return 0; return m_offsetOfHitCharacter; }private: Vector<SVGChar>::iterator m_character; float m_distance; int m_x; int m_y; int m_offsetOfHitCharacter;};// Helper class for selectionRect()struct SVGInlineTextBoxSelectionRectWalker { SVGInlineTextBoxSelectionRectWalker() { } void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) { RenderStyle* style = textBox->textRenderer()->style(); for (Vector<SVGChar>::iterator it = start; it != end; ++it) { if (it->isHidden()) continue; unsigned int newOffset = textBox->start() + (it - start) + startOffset; m_selectionRect.unite(textBox->calculateGlyphBoundaries(style, newOffset, *it)); } m_selectionRect = chunkCtm.mapRect(m_selectionRect); } FloatRect selectionRect() const { return m_selectionRect; }private: FloatRect m_selectionRect;};SVGChar* SVGInlineTextBox::closestCharacterToPosition(int x, int y, int& offsetOfHitCharacter) const{ SVGRootInlineBox* rootBox = svgRootInlineBox(); if (!rootBox) return 0; SVGInlineTextBoxClosestCharacterToPositionWalker walkerCallback(x, y); SVGTextChunkWalker<SVGInlineTextBoxClosestCharacterToPositionWalker> walker(&walkerCallback, &SVGInlineTextBoxClosestCharacterToPositionWalker::chunkPortionCallback); rootBox->walkTextChunks(&walker, this); offsetOfHitCharacter = walkerCallback.offsetOfHitCharacter(); return walkerCallback.character();}bool SVGInlineTextBox::svgCharacterHitsPosition(int x, int y, int& closestOffsetInBox) const{ int offsetOfHitCharacter = 0; SVGChar* charAtPosPtr = closestCharacterToPosition(x, y, offsetOfHitCharacter); if (!charAtPosPtr) return false; SVGChar& charAtPos = *charAtPosPtr; RenderStyle* style = textRenderer()->style(m_firstLine); FloatRect glyphRect = calculateGlyphBoundaries(style, offsetOfHitCharacter, charAtPos); // FIXME: Why? if (direction() == RTL) offsetOfHitCharacter++; // The caller actually the closest offset before/after the hit char // closestCharacterToPosition returns us offsetOfHitCharacter. closestOffsetInBox = offsetOfHitCharacter; // FIXME: (bug 13910) This code does not handle bottom-to-top/top-to-bottom vertical text. // Check whether y position hits the current character if (y < charAtPos.y - glyphRect.height() || y > charAtPos.y) return false; // Check whether x position hits the current character if (x < charAtPos.x) { if (closestOffsetInBox > 0 && direction() == LTR) return true; else if (closestOffsetInBox < (int) end() && direction() == RTL) return true; return false; } // Adjust the closest offset to after the char if x was after the char midpoint if (x >= charAtPos.x + glyphRect.width() / 2.0) closestOffsetInBox += direction() == RTL ? -1 : 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -