📄 svgrootinlinebox.cpp
字号:
/* * This file is part of the WebKit project. * * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> * (C) 2006 Apple Computer Inc. * (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 "SVGRootInlineBox.h"#include "Editor.h"#include "Frame.h"#include "GraphicsContext.h"#include "RenderBlock.h"#include "RenderSVGRoot.h"#include "SVGInlineFlowBox.h"#include "SVGInlineTextBox.h"#include "SVGFontElement.h"#include "SVGPaintServer.h"#include "SVGRenderStyleDefs.h"#include "SVGRenderSupport.h"#include "SVGResourceFilter.h"#include "SVGTextPositioningElement.h"#include "SVGURIReference.h"#include "Text.h"#include "UnicodeRange.h"#include <float.h>// Text chunk creation is complex and the whole process// can easily be traced by setting this variable > 0.#define DEBUG_CHUNK_BUILDING 0namespace WebCore {static inline bool isVerticalWritingMode(const SVGRenderStyle* style){ return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB; }static inline EAlignmentBaseline dominantBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font){ ASSERT(text); const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0; ASSERT(style); const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0; EDominantBaseline baseline = style->dominantBaseline(); if (baseline == DB_AUTO) { if (isVerticalText) baseline = DB_CENTRAL; else baseline = DB_ALPHABETIC; } switch (baseline) { case DB_USE_SCRIPT: // TODO: The dominant-baseline and the baseline-table components are set by // determining the predominant script of the character data content. return AB_ALPHABETIC; case DB_NO_CHANGE: { if (parentStyle) return dominantBaselineToShift(isVerticalText, text->parent(), font); ASSERT_NOT_REACHED(); return AB_AUTO; } case DB_RESET_SIZE: { if (parentStyle) return dominantBaselineToShift(isVerticalText, text->parent(), font); ASSERT_NOT_REACHED(); return AB_AUTO; } case DB_IDEOGRAPHIC: return AB_IDEOGRAPHIC; case DB_ALPHABETIC: return AB_ALPHABETIC; case DB_HANGING: return AB_HANGING; case DB_MATHEMATICAL: return AB_MATHEMATICAL; case DB_CENTRAL: return AB_CENTRAL; case DB_MIDDLE: return AB_MIDDLE; case DB_TEXT_AFTER_EDGE: return AB_TEXT_AFTER_EDGE; case DB_TEXT_BEFORE_EDGE: return AB_TEXT_BEFORE_EDGE; default: ASSERT_NOT_REACHED(); return AB_AUTO; }}static inline float alignmentBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font){ ASSERT(text); const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0; ASSERT(style); const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0; EAlignmentBaseline baseline = style->alignmentBaseline(); if (baseline == AB_AUTO) { if (parentStyle && style->dominantBaseline() == DB_AUTO) baseline = dominantBaselineToShift(isVerticalText, text->parent(), font); else baseline = dominantBaselineToShift(isVerticalText, text, font); ASSERT(baseline != AB_AUTO); } // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling switch (baseline) { case AB_BASELINE: { if (parentStyle) return dominantBaselineToShift(isVerticalText, text->parent(), font); return 0.0f; } case AB_BEFORE_EDGE: case AB_TEXT_BEFORE_EDGE: return font.ascent(); case AB_MIDDLE: return font.xHeight() / 2.0f; case AB_CENTRAL: // Not needed, we're taking this into account already for vertical text! // return (font.ascent() - font.descent()) / 2.0f; return 0.0f; case AB_AFTER_EDGE: case AB_TEXT_AFTER_EDGE: case AB_IDEOGRAPHIC: return font.descent(); case AB_ALPHABETIC: return 0.0f; case AB_HANGING: return font.ascent() * 8.0f / 10.0f; case AB_MATHEMATICAL: return font.ascent() / 2.0f; default: ASSERT_NOT_REACHED(); return 0.0f; }}static inline float glyphOrientationToAngle(const SVGRenderStyle* svgStyle, bool isVerticalText, const UChar& character){ switch (isVerticalText ? svgStyle->glyphOrientationVertical() : svgStyle->glyphOrientationHorizontal()) { case GO_AUTO: { // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees. // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees. unsigned int unicodeRange = findCharUnicodeRange(character); if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic) return 90.0f; return 0.0f; } case GO_90DEG: return 90.0f; case GO_180DEG: return 180.0f; case GO_270DEG: return 270.0f; case GO_0DEG: default: return 0.0f; }}static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle){ return fabsf(fmodf(orientationAngle, 180.0f)) == 0.0f;}static inline float calculateGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float orientationAngle, float glyphWidth, float glyphHeight, const Font& font, SVGChar& svgChar, float& xOrientationShift, float& yOrientationShift){ bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(orientationAngle); // The function is based on spec requirements: // // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph. // // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of // 180 degrees,then the current text position is incremented according to the horizontal metrics of the glyph. // vertical orientation handling if (isVerticalText) { if (orientationAngle == 0.0f) { xOrientationShift = -glyphWidth / 2.0f; yOrientationShift = font.ascent(); } else if (orientationAngle == 90.0f) { xOrientationShift = -glyphHeight; yOrientationShift = font.descent(); svgChar.orientationShiftY = -font.ascent(); } else if (orientationAngle == 270.0f) { xOrientationShift = glyphHeight; yOrientationShift = font.descent(); svgChar.orientationShiftX = -glyphWidth; svgChar.orientationShiftY = -font.ascent(); } else if (orientationAngle == 180.0f) { yOrientationShift = font.ascent(); svgChar.orientationShiftX = -glyphWidth / 2.0f; svgChar.orientationShiftY = font.ascent() - font.descent(); } // vertical advance calculation if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees) return glyphWidth; return glyphHeight; } // horizontal orientation handling if (orientationAngle == 90.0f) { xOrientationShift = glyphWidth / 2.0f; yOrientationShift = -font.descent(); svgChar.orientationShiftX = -glyphWidth / 2.0f - font.descent(); svgChar.orientationShiftY = font.descent(); } else if (orientationAngle == 270.0f) { xOrientationShift = -glyphWidth / 2.0f; yOrientationShift = -font.descent(); svgChar.orientationShiftX = -glyphWidth / 2.0f + font.descent(); svgChar.orientationShiftY = glyphHeight; } else if (orientationAngle == 180.0f) { xOrientationShift = glyphWidth / 2.0f; svgChar.orientationShiftX = -glyphWidth / 2.0f; svgChar.orientationShiftY = font.ascent() - font.descent(); } // horizontal advance calculation if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees) return glyphHeight; return glyphWidth;}static inline void startTextChunk(SVGTextChunkLayoutInfo& info){ info.chunk.boxes.clear(); info.chunk.boxes.append(SVGInlineBoxCharacterRange()); info.chunk.start = info.it; info.assignChunkProperties = true;}static inline void closeTextChunk(SVGTextChunkLayoutInfo& info){ ASSERT(!info.chunk.boxes.last().isOpen()); ASSERT(info.chunk.boxes.last().isClosed()); info.chunk.end = info.it; ASSERT(info.chunk.end >= info.chunk.start); info.svgTextChunks.append(info.chunk);}RenderSVGRoot* findSVGRootObject(RenderObject* start){ // Find associated root inline box while (start && !start->isSVGRoot()) start = start->parent(); ASSERT(start); ASSERT(start->isSVGRoot()); return static_cast<RenderSVGRoot*>(start);}static inline FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>& chars){ return topLeftPositionOfCharacterRange(chars.begin(), chars.end());}FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator it, Vector<SVGChar>::iterator end){ float lowX = FLT_MAX, lowY = FLT_MAX; for (; it != end; ++it) { if (it->isHidden()) continue; float x = (*it).x; float y = (*it).y; if (x < lowX) lowX = x; if (y < lowY) lowY = y; } return FloatPoint(lowX, lowY);}// Helper functionstatic float calculateKerning(RenderObject* item){ const Font& font = item->style()->font(); const SVGRenderStyle* svgStyle = item->style()->svgStyle(); float kerning = 0.0f; if (CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->kerning())) { kerning = primitive->getFloatValue(); if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE && font.pixelSize() > 0) kerning = kerning / 100.0f * font.pixelSize(); } return kerning;}// Helper class for paint()struct SVGRootInlineBoxPaintWalker { SVGRootInlineBoxPaintWalker(SVGRootInlineBox* rootBox, SVGResourceFilter* rootFilter, RenderObject::PaintInfo paintInfo, int tx, int ty) : m_rootBox(rootBox) , m_chunkStarted(false) , m_paintInfo(paintInfo) , m_savedInfo(paintInfo) , m_boundingBox(tx + rootBox->x(), ty + rootBox->y(), rootBox->width(), rootBox->height()) , m_filter(0) , m_rootFilter(rootFilter) , m_fillPaintServer(0) , m_strokePaintServer(0) , m_fillPaintServerObject(0) , m_strokePaintServerObject(0) , m_tx(tx) , m_ty(ty) { } ~SVGRootInlineBoxPaintWalker() { ASSERT(!m_filter); ASSERT(!m_fillPaintServer); ASSERT(!m_fillPaintServerObject); ASSERT(!m_strokePaintServer); ASSERT(!m_strokePaintServerObject); ASSERT(!m_chunkStarted); } void teardownFillPaintServer() { if (!m_fillPaintServer) return; m_fillPaintServer->teardown(m_paintInfo.context, m_fillPaintServerObject, ApplyToFillTargetType, true); m_fillPaintServer = 0; m_fillPaintServerObject = 0; } void teardownStrokePaintServer() { if (!m_strokePaintServer) return; m_strokePaintServer->teardown(m_paintInfo.context, m_strokePaintServerObject, ApplyToStrokeTargetType, true); m_strokePaintServer = 0; m_strokePaintServerObject = 0; } void chunkStartCallback(InlineBox* box) { ASSERT(!m_chunkStarted); m_chunkStarted = true; InlineFlowBox* flowBox = box->parent(); // Initialize text rendering RenderObject* object = flowBox->renderer(); ASSERT(object); m_savedInfo = m_paintInfo; m_paintInfo.context->save(); if (!flowBox->isRootInlineBox()) m_paintInfo.context->concatCTM(m_rootBox->renderer()->localTransform()); m_paintInfo.context->concatCTM(object->localTransform()); if (!flowBox->isRootInlineBox()) { prepareToRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_rootFilter); m_paintInfo.rect = object->localTransform().inverse().mapRect(m_paintInfo.rect); } } void chunkEndCallback(InlineBox* box) { ASSERT(m_chunkStarted); m_chunkStarted = false; InlineFlowBox* flowBox = box->parent(); RenderObject* object = flowBox->renderer(); ASSERT(object); // Clean up last used paint server teardownFillPaintServer(); teardownStrokePaintServer();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -