summaryrefslogtreecommitdiffstats
path: root/WebCore/svg/SVGFont.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/svg/SVGFont.cpp')
-rw-r--r--WebCore/svg/SVGFont.cpp594
1 files changed, 594 insertions, 0 deletions
diff --git a/WebCore/svg/SVGFont.cpp b/WebCore/svg/SVGFont.cpp
new file mode 100644
index 0000000..ece667f
--- /dev/null
+++ b/WebCore/svg/SVGFont.cpp
@@ -0,0 +1,594 @@
+/**
+ * Copyright (C) 2007, 2008 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_FONTS)
+#include "Font.h"
+
+#include "CSSFontSelector.h"
+#include "GraphicsContext.h"
+#include "RenderObject.h"
+#include "SimpleFontData.h"
+#include "SVGAltGlyphElement.h"
+#include "SVGFontData.h"
+#include "SVGGlyphElement.h"
+#include "SVGGlyphMap.h"
+#include "SVGFontElement.h"
+#include "SVGFontFaceElement.h"
+#include "SVGMissingGlyphElement.h"
+#include "SVGPaintServer.h"
+#include "SVGPaintServerSolid.h"
+#include "XMLNames.h"
+
+using namespace WTF::Unicode;
+
+namespace WebCore {
+
+static inline float convertEmUnitToPixel(float fontSize, float unitsPerEm, float value)
+{
+ if (unitsPerEm == 0.0f)
+ return 0.0f;
+
+ return value * fontSize / unitsPerEm;
+}
+
+static inline bool isVerticalWritingMode(const SVGRenderStyle* style)
+{
+ return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB;
+}
+
+// Helper functions to determine the arabic character forms (initial, medial, terminal, isolated)
+enum ArabicCharShapingMode {
+ SNone = 0,
+ SRight = 1,
+ SDual = 2
+};
+
+static const ArabicCharShapingMode s_arabicCharShapingMode[222] = {
+ SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, SDual , SDual , SDual , SDual , SDual , SRight, /* 0x0622 - 0x062F */
+ SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SNone , SNone , SNone , SNone , SNone , /* 0x0630 - 0x063F */
+ SNone , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SDual , SDual , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0640 - 0x064F */
+ SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0650 - 0x065F */
+ SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0660 - 0x066F */
+ SNone , SRight, SRight, SRight, SNone , SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0670 - 0x067F */
+ SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, /* 0x0680 - 0x068F */
+ SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0690 - 0x069F */
+ SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06A0 - 0x06AF */
+ SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06B0 - 0x06BF */
+ SRight, SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, /* 0x06C0 - 0x06CF */
+ SDual , SDual , SRight, SRight, SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06D0 - 0x06DF */
+ SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06E0 - 0x06EF */
+ SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SDual , SDual , SDual , SNone , SNone , SNone /* 0x06F0 - 0x06FF */
+};
+
+static inline SVGGlyphIdentifier::ArabicForm processArabicFormDetection(const UChar& curChar, bool& lastCharShapesRight, SVGGlyphIdentifier::ArabicForm* prevForm)
+{
+ SVGGlyphIdentifier::ArabicForm curForm;
+
+ ArabicCharShapingMode shapingMode = SNone;
+ if (curChar >= 0x0622 && curChar <= 0x06FF)
+ shapingMode = s_arabicCharShapingMode[curChar - 0x0622];
+
+ // Use a simple state machine to identify the actual arabic form
+ // It depends on the order of the arabic form enum:
+ // enum ArabicForm { None = 0, Isolated, Terminal, Initial, Medial };
+
+ if (lastCharShapesRight && shapingMode == SDual) {
+ if (prevForm) {
+ int correctedForm = (int) *prevForm + 1;
+ ASSERT(correctedForm >= SVGGlyphIdentifier::None && correctedForm <= SVGGlyphIdentifier::Medial);
+ *prevForm = static_cast<SVGGlyphIdentifier::ArabicForm>(correctedForm);
+ }
+
+ curForm = SVGGlyphIdentifier::Initial;
+ } else
+ curForm = shapingMode == SNone ? SVGGlyphIdentifier::None : SVGGlyphIdentifier::Isolated;
+
+ lastCharShapesRight = shapingMode != SNone;
+ return curForm;
+}
+
+static Vector<SVGGlyphIdentifier::ArabicForm> charactersWithArabicForm(const String& input, bool rtl)
+{
+ Vector<SVGGlyphIdentifier::ArabicForm> forms;
+ unsigned length = input.length();
+
+ bool containsArabic = false;
+ for (unsigned i = 0; i < length; ++i) {
+ if (isArabicChar(input[i])) {
+ containsArabic = true;
+ break;
+ }
+ }
+
+ if (!containsArabic)
+ return forms;
+
+ bool lastCharShapesRight = false;
+
+ // Start identifying arabic forms
+ if (rtl) {
+ for (int i = length - 1; i >= 0; --i)
+ forms.prepend(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.first()));
+ } else {
+ for (unsigned i = 0; i < length; ++i)
+ forms.append(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.last()));
+ }
+
+ return forms;
+}
+
+static inline bool isCompatibleArabicForm(const SVGGlyphIdentifier& identifier, const Vector<SVGGlyphIdentifier::ArabicForm>& chars, unsigned startPosition, unsigned endPosition)
+{
+ if (chars.isEmpty())
+ return true;
+
+ Vector<SVGGlyphIdentifier::ArabicForm>::const_iterator it = chars.begin() + startPosition;
+ Vector<SVGGlyphIdentifier::ArabicForm>::const_iterator end = chars.begin() + endPosition;
+
+ ASSERT(end <= chars.end());
+ for (; it != end; ++it) {
+ if (*it != static_cast<SVGGlyphIdentifier::ArabicForm>(identifier.arabicForm) && *it != SVGGlyphIdentifier::None)
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool isCompatibleGlyph(const SVGGlyphIdentifier& identifier, bool isVerticalText, const String& language,
+ const Vector<SVGGlyphIdentifier::ArabicForm>& chars, unsigned startPosition, unsigned endPosition)
+{
+ bool valid = true;
+
+ // Check wheter orientation if glyph fits within the request
+ switch (identifier.orientation) {
+ case SVGGlyphIdentifier::Vertical:
+ valid = isVerticalText;
+ break;
+ case SVGGlyphIdentifier::Horizontal:
+ valid = !isVerticalText;
+ break;
+ case SVGGlyphIdentifier::Both:
+ break;
+ }
+
+ if (!valid)
+ return false;
+
+ // Check wheter languages are compatible
+ if (!identifier.languages.isEmpty()) {
+ // This glyph exists only in certain languages, if we're not specifying a
+ // language on the referencing element we're unable to use this glyph.
+ if (language.isEmpty())
+ return false;
+
+ // Split subcode from language, if existant.
+ String languagePrefix;
+
+ int subCodeSeparator = language.find('-');
+ if (subCodeSeparator != -1)
+ languagePrefix = language.left(subCodeSeparator);
+
+ Vector<String>::const_iterator it = identifier.languages.begin();
+ Vector<String>::const_iterator end = identifier.languages.end();
+
+ bool found = false;
+ for (; it != end; ++it) {
+ const String& cur = *it;
+ if (cur == language || cur == languagePrefix) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return false;
+ }
+
+ // Check wheter arabic form is compatible
+ return isCompatibleArabicForm(identifier, chars, startPosition, endPosition);
+}
+
+static inline const SVGFontData* svgFontAndFontFaceElementForFontData(const SimpleFontData* fontData, SVGFontFaceElement*& fontFace, SVGFontElement*& font)
+{
+ ASSERT(fontData->isCustomFont());
+ ASSERT(fontData->isSVGFont());
+
+ const SVGFontData* svgFontData = static_cast<const SVGFontData*>(fontData->svgFontData());
+
+ fontFace = svgFontData->svgFontFaceElement();
+ ASSERT(fontFace);
+
+ font = fontFace->associatedFontElement();
+ return svgFontData;
+}
+
+// Helper class to walk a text run. Lookup a SVGGlyphIdentifier for each character
+// - also respecting possibly defined ligatures - and invoke a callback for each found glyph.
+template<typename SVGTextRunData>
+struct SVGTextRunWalker {
+ typedef bool (*SVGTextRunWalkerCallback)(const SVGGlyphIdentifier&, SVGTextRunData&);
+ typedef void (*SVGTextRunWalkerMissingGlyphCallback)(const TextRun&, SVGTextRunData&);
+
+ SVGTextRunWalker(const SVGFontData* fontData, SVGFontElement* fontElement, SVGTextRunData& data,
+ SVGTextRunWalkerCallback callback, SVGTextRunWalkerMissingGlyphCallback missingGlyphCallback)
+ : m_fontData(fontData)
+ , m_fontElement(fontElement)
+ , m_walkerData(data)
+ , m_walkerCallback(callback)
+ , m_walkerMissingGlyphCallback(missingGlyphCallback)
+ {
+ }
+
+ void walk(const TextRun& run, bool isVerticalText, const String& language, int from, int to)
+ {
+ // Should hold true for SVG text, otherwhise sth. is wrong
+ ASSERT(to - from == run.length());
+
+ Vector<SVGGlyphIdentifier::ArabicForm> chars(charactersWithArabicForm(String(run.data(from), run.length()), run.rtl()));
+
+ SVGGlyphIdentifier identifier;
+ bool foundGlyph = false;
+ int characterLookupRange;
+ int endOfScanRange = to + m_walkerData.extraCharsAvailable;
+
+ bool haveAltGlyph = false;
+ SVGGlyphIdentifier altGlyphIdentifier;
+ if (RenderObject* renderObject = run.referencingRenderObject()) {
+ if (renderObject->element() && renderObject->element()->hasTagName(SVGNames::altGlyphTag)) {
+ SVGGlyphElement* glyphElement = static_cast<SVGAltGlyphElement*>(renderObject->element())->glyphElement();
+ if (glyphElement) {
+ haveAltGlyph = true;
+ altGlyphIdentifier = glyphElement->buildGlyphIdentifier();
+ altGlyphIdentifier.isValid = true;
+ altGlyphIdentifier.nameLength = to - from;
+ }
+ }
+ }
+
+ for (int i = from; i < to; ++i) {
+ // If characterLookupRange is > 0, then the font defined ligatures (length of unicode property value > 1).
+ // We have to check wheter the current character & the next character define a ligature. This needs to be
+ // extended to the n-th next character (where n is 'characterLookupRange'), to check for any possible ligature.
+ characterLookupRange = endOfScanRange - i;
+
+ String lookupString(run.data(i), characterLookupRange);
+ Vector<SVGGlyphIdentifier> glyphs;
+ if (haveAltGlyph)
+ glyphs.append(altGlyphIdentifier);
+ else
+ m_fontElement->getGlyphIdentifiersForString(lookupString, glyphs);
+
+ Vector<SVGGlyphIdentifier>::iterator it = glyphs.begin();
+ Vector<SVGGlyphIdentifier>::iterator end = glyphs.end();
+
+ for (; it != end; ++it) {
+ identifier = *it;
+ if (identifier.isValid && isCompatibleGlyph(identifier, isVerticalText, language, chars, i, i + identifier.nameLength)) {
+ ASSERT(characterLookupRange > 0);
+ i += identifier.nameLength - 1;
+ m_walkerData.charsConsumed += identifier.nameLength;
+ m_walkerData.glyphName = identifier.glyphName;
+
+ foundGlyph = true;
+ SVGGlyphElement::inheritUnspecifiedAttributes(identifier, m_fontData);
+ break;
+ }
+ }
+
+ if (!foundGlyph) {
+ ++m_walkerData.charsConsumed;
+ if (SVGMissingGlyphElement* element = m_fontElement->firstMissingGlyphElement()) {
+ // <missing-glyph> element support
+ identifier = SVGGlyphElement::buildGenericGlyphIdentifier(element);
+ SVGGlyphElement::inheritUnspecifiedAttributes(identifier, m_fontData);
+ identifier.isValid = true;
+ } else {
+ // Fallback to system font fallback
+ TextRun subRun(run);
+ subRun.setText(subRun.data(i), 1);
+
+ (*m_walkerMissingGlyphCallback)(subRun, m_walkerData);
+ continue;
+ }
+ }
+
+ if (!(*m_walkerCallback)(identifier, m_walkerData))
+ break;
+
+ foundGlyph = false;
+ }
+ }
+
+private:
+ const SVGFontData* m_fontData;
+ SVGFontElement* m_fontElement;
+ SVGTextRunData& m_walkerData;
+ SVGTextRunWalkerCallback m_walkerCallback;
+ SVGTextRunWalkerMissingGlyphCallback m_walkerMissingGlyphCallback;
+};
+
+// Callback & data structures to compute the width of text using SVG Fonts
+struct SVGTextRunWalkerMeasuredLengthData {
+ int at;
+ int from;
+ int to;
+ int extraCharsAvailable;
+ int charsConsumed;
+ String glyphName;
+
+ float scale;
+ float length;
+ const Font* font;
+};
+
+bool floatWidthUsingSVGFontCallback(const SVGGlyphIdentifier& identifier, SVGTextRunWalkerMeasuredLengthData& data)
+{
+ if (data.at >= data.from && data.at < data.to)
+ data.length += identifier.horizontalAdvanceX * data.scale;
+
+ data.at++;
+ return data.at < data.to;
+}
+
+void floatWidthMissingGlyphCallback(const TextRun& run, SVGTextRunWalkerMeasuredLengthData& data)
+{
+ // Handle system font fallback
+ FontDescription fontDescription(data.font->fontDescription());
+ fontDescription.setFamily(FontFamily());
+ Font font(fontDescription, 0, 0); // spacing handled by SVG text code.
+ font.update(data.font->fontSelector());
+
+ data.length += font.floatWidth(run);
+}
+
+
+SVGFontElement* Font::svgFont() const
+{
+ if (!isSVGFont())
+ return 0;
+
+ SVGFontElement* fontElement = 0;
+ SVGFontFaceElement* fontFaceElement = 0;
+ if (svgFontAndFontFaceElementForFontData(primaryFont(), fontFaceElement, fontElement))
+ return fontElement;
+
+ return 0;
+}
+
+static float floatWidthOfSubStringUsingSVGFont(const Font* font, const TextRun& run, int extraCharsAvailable, int from, int to, int& charsConsumed, String& glyphName)
+{
+ int newFrom = to > from ? from : to;
+ int newTo = to > from ? to : from;
+
+ from = newFrom;
+ to = newTo;
+
+ SVGFontElement* fontElement = 0;
+ SVGFontFaceElement* fontFaceElement = 0;
+
+ if (const SVGFontData* fontData = svgFontAndFontFaceElementForFontData(font->primaryFont(), fontFaceElement, fontElement)) {
+ if (!fontElement)
+ return 0.0f;
+
+ SVGTextRunWalkerMeasuredLengthData data;
+
+ data.font = font;
+ data.at = from;
+ data.from = from;
+ data.to = to;
+ data.extraCharsAvailable = extraCharsAvailable;
+ data.charsConsumed = 0;
+ data.scale = convertEmUnitToPixel(font->size(), fontFaceElement->unitsPerEm(), 1.0f);
+ data.length = 0.0f;
+
+ String language;
+ bool isVerticalText = false; // Holds true for HTML text
+
+ // TODO: language matching & svg glyphs should be possible for HTML text, too.
+ if (RenderObject* renderObject = run.referencingRenderObject()) {
+ isVerticalText = isVerticalWritingMode(renderObject->style()->svgStyle());
+
+ if (SVGElement* element = static_cast<SVGElement*>(renderObject->element()))
+ language = element->getAttribute(XMLNames::langAttr);
+ }
+
+ SVGTextRunWalker<SVGTextRunWalkerMeasuredLengthData> runWalker(fontData, fontElement, data, floatWidthUsingSVGFontCallback, floatWidthMissingGlyphCallback);
+ runWalker.walk(run, isVerticalText, language, 0, run.length());
+ charsConsumed = data.charsConsumed;
+ glyphName = data.glyphName;
+ return data.length;
+ }
+
+ return 0.0f;
+}
+
+float Font::floatWidthUsingSVGFont(const TextRun& run) const
+{
+ int charsConsumed;
+ String glyphName;
+ return floatWidthOfSubStringUsingSVGFont(this, run, 0, 0, run.length(), charsConsumed, glyphName);
+}
+
+float Font::floatWidthUsingSVGFont(const TextRun& run, int extraCharsAvailable, int& charsConsumed, String& glyphName) const
+{
+ return floatWidthOfSubStringUsingSVGFont(this, run, extraCharsAvailable, 0, run.length(), charsConsumed, glyphName);
+}
+
+// Callback & data structures to draw text using SVG Fonts
+struct SVGTextRunWalkerDrawTextData {
+ int extraCharsAvailable;
+ int charsConsumed;
+ String glyphName;
+ Vector<SVGGlyphIdentifier> glyphIdentifiers;
+ Vector<UChar> fallbackCharacters;
+};
+
+bool drawTextUsingSVGFontCallback(const SVGGlyphIdentifier& identifier, SVGTextRunWalkerDrawTextData& data)
+{
+ data.glyphIdentifiers.append(identifier);
+ return true;
+}
+
+void drawTextMissingGlyphCallback(const TextRun& run, SVGTextRunWalkerDrawTextData& data)
+{
+ ASSERT(run.length() == 1);
+ data.glyphIdentifiers.append(SVGGlyphIdentifier());
+ data.fallbackCharacters.append(run[0]);
+}
+
+void Font::drawTextUsingSVGFont(GraphicsContext* context, const TextRun& run,
+ const FloatPoint& point, int from, int to) const
+{
+ SVGFontElement* fontElement = 0;
+ SVGFontFaceElement* fontFaceElement = 0;
+
+ if (const SVGFontData* fontData = svgFontAndFontFaceElementForFontData(primaryFont(), fontFaceElement, fontElement)) {
+ if (!fontElement)
+ return;
+
+ SVGTextRunWalkerDrawTextData data;
+ FloatPoint currentPoint = point;
+ float scale = convertEmUnitToPixel(size(), fontFaceElement->unitsPerEm(), 1.0f);
+
+ SVGPaintServer* activePaintServer = run.activePaintServer();
+
+ // If renderObject is not set, we're dealing for HTML text rendered using SVG Fonts.
+ if (!run.referencingRenderObject()) {
+ ASSERT(!activePaintServer);
+
+ // TODO: We're only supporting simple filled HTML text so far.
+ SVGPaintServerSolid* solidPaintServer = SVGPaintServer::sharedSolidPaintServer();
+ solidPaintServer->setColor(context->fillColor());
+
+ activePaintServer = solidPaintServer;
+ }
+
+ ASSERT(activePaintServer);
+
+ int charsConsumed;
+ String glyphName;
+ bool isVerticalText = false;
+ float xStartOffset = floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName);
+ FloatPoint glyphOrigin;
+
+ String language;
+
+ // TODO: language matching & svg glyphs should be possible for HTML text, too.
+ if (run.referencingRenderObject()) {
+ isVerticalText = isVerticalWritingMode(run.referencingRenderObject()->style()->svgStyle());
+
+ if (SVGElement* element = static_cast<SVGElement*>(run.referencingRenderObject()->element()))
+ language = element->getAttribute(XMLNames::langAttr);
+ }
+
+ if (!isVerticalText) {
+ glyphOrigin.setX(fontData->horizontalOriginX() * scale);
+ glyphOrigin.setY(fontData->horizontalOriginY() * scale);
+ }
+
+ data.extraCharsAvailable = 0;
+
+ SVGTextRunWalker<SVGTextRunWalkerDrawTextData> runWalker(fontData, fontElement, data, drawTextUsingSVGFontCallback, drawTextMissingGlyphCallback);
+ runWalker.walk(run, isVerticalText, language, from, to);
+
+ SVGPaintTargetType targetType = context->textDrawingMode() == cTextStroke ? ApplyToStrokeTargetType : ApplyToFillTargetType;
+
+ unsigned numGlyphs = data.glyphIdentifiers.size();
+ unsigned fallbackCharacterIndex = 0;
+ for (unsigned i = 0; i < numGlyphs; ++i) {
+ const SVGGlyphIdentifier& identifier = data.glyphIdentifiers[run.rtl() ? numGlyphs - i - 1 : i];
+ if (identifier.isValid) {
+ // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations).
+ if (!identifier.pathData.isEmpty()) {
+ context->save();
+
+ if (isVerticalText) {
+ glyphOrigin.setX(identifier.verticalOriginX * scale);
+ glyphOrigin.setY(identifier.verticalOriginY * scale);
+ }
+
+ context->translate(xStartOffset + currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y());
+ context->scale(FloatSize(scale, -scale));
+
+ context->beginPath();
+ context->addPath(identifier.pathData);
+
+ if (activePaintServer->setup(context, run.referencingRenderObject(), targetType)) {
+ // Spec: Any properties specified on a text elements which represents a length, such as the
+ // 'stroke-width' property, might produce surprising results since the length value will be
+ // processed in the coordinate system of the glyph. (TODO: What other lengths? miter-limit? dash-offset?)
+ if (targetType == ApplyToStrokeTargetType && scale != 0.0f)
+ context->setStrokeThickness(context->strokeThickness() / scale);
+
+ activePaintServer->renderPath(context, run.referencingRenderObject(), targetType);
+ activePaintServer->teardown(context, run.referencingRenderObject(), targetType);
+ }
+
+ context->restore();
+ }
+
+ if (isVerticalText)
+ currentPoint.move(0.0f, identifier.verticalAdvanceY * scale);
+ else
+ currentPoint.move(identifier.horizontalAdvanceX * scale, 0.0f);
+ } else {
+ // Handle system font fallback
+ FontDescription fontDescription(context->font().fontDescription());
+ fontDescription.setFamily(FontFamily());
+ Font font(fontDescription, 0, 0); // spacing handled by SVG text code.
+ font.update(context->font().fontSelector());
+
+ TextRun fallbackCharacterRun(run);
+ fallbackCharacterRun.setText(&data.fallbackCharacters[run.rtl() ? data.fallbackCharacters.size() - fallbackCharacterIndex - 1 : fallbackCharacterIndex], 1);
+ font.drawText(context, fallbackCharacterRun, currentPoint);
+
+ if (isVerticalText)
+ currentPoint.move(0.0f, font.floatWidth(fallbackCharacterRun));
+ else
+ currentPoint.move(font.floatWidth(fallbackCharacterRun), 0.0f);
+
+ fallbackCharacterIndex++;
+ }
+ }
+ }
+}
+
+FloatRect Font::selectionRectForTextUsingSVGFont(const TextRun& run, const IntPoint& point, int height, int from, int to) const
+{
+ int charsConsumed;
+ String glyphName;
+
+ return FloatRect(point.x() + floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName),
+ point.y(), floatWidthOfSubStringUsingSVGFont(this, run, 0, from, to, charsConsumed, glyphName), height);
+}
+
+int Font::offsetForPositionForTextUsingSVGFont(const TextRun&, int position, bool includePartialGlyphs) const
+{
+ // TODO: Fix text selection when HTML text is drawn using a SVG Font
+ // We need to integrate the SVG text selection code in the offsetForPosition() framework.
+ // This will also fix a major issue, that SVG Text code can't select arabic strings properly.
+ return 0;
+}
+
+}
+
+#endif