1 /* 2 * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 */ 20 21 #include "config.h" 22 23 #if ENABLE(SVG_FONTS) 24 #include "Font.h" 25 26 #include "CSSFontSelector.h" 27 #include "GraphicsContext.h" 28 #include "RenderObject.h" 29 #include "RenderSVGInlineText.h" 30 #include "RenderSVGResourceSolidColor.h" 31 #include "SVGAltGlyphElement.h" 32 #include "SVGFontData.h" 33 #include "SVGFontElement.h" 34 #include "SVGFontFaceElement.h" 35 #include "SVGGlyphElement.h" 36 #include "SVGGlyphMap.h" 37 #include "SVGMissingGlyphElement.h" 38 #include "SVGNames.h" 39 #include "SimpleFontData.h" 40 #include "TextRun.h" 41 #include "XMLNames.h" 42 43 using namespace WTF::Unicode; 44 45 namespace WebCore { 46 47 static inline float convertEmUnitToPixel(float fontSize, float unitsPerEm, float value) 48 { 49 if (!unitsPerEm) 50 return 0.0f; 51 52 return value * fontSize / unitsPerEm; 53 } 54 55 static inline bool isVerticalWritingMode(const SVGRenderStyle* style) 56 { 57 return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB; 58 } 59 60 // Helper functions to determine the arabic character forms (initial, medial, terminal, isolated) 61 enum ArabicCharShapingMode { 62 SNone = 0, 63 SRight = 1, 64 SDual = 2 65 }; 66 67 static const ArabicCharShapingMode s_arabicCharShapingMode[222] = { 68 SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, SDual , SDual , SDual , SDual , SDual , SRight, /* 0x0622 - 0x062F */ 69 SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SNone , SNone , SNone , SNone , SNone , /* 0x0630 - 0x063F */ 70 SNone , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SDual , SDual , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0640 - 0x064F */ 71 SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0650 - 0x065F */ 72 SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x0660 - 0x066F */ 73 SNone , SRight, SRight, SRight, SNone , SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0670 - 0x067F */ 74 SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, /* 0x0680 - 0x068F */ 75 SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SDual , SDual , SDual , SDual , SDual , /* 0x0690 - 0x069F */ 76 SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06A0 - 0x06AF */ 77 SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , SDual , /* 0x06B0 - 0x06BF */ 78 SRight, SDual , SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SRight, SDual , SRight, SDual , SRight, /* 0x06C0 - 0x06CF */ 79 SDual , SDual , SRight, SRight, SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06D0 - 0x06DF */ 80 SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , /* 0x06E0 - 0x06EF */ 81 SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SNone , SDual , SDual , SDual , SNone , SNone , SNone /* 0x06F0 - 0x06FF */ 82 }; 83 84 static inline SVGGlyphIdentifier::ArabicForm processArabicFormDetection(const UChar& curChar, bool& lastCharShapesRight, SVGGlyphIdentifier::ArabicForm* prevForm) 85 { 86 SVGGlyphIdentifier::ArabicForm curForm; 87 88 ArabicCharShapingMode shapingMode = SNone; 89 if (curChar >= 0x0622 && curChar <= 0x06FF) 90 shapingMode = s_arabicCharShapingMode[curChar - 0x0622]; 91 92 // Use a simple state machine to identify the actual arabic form 93 // It depends on the order of the arabic form enum: 94 // enum ArabicForm { None = 0, Isolated, Terminal, Initial, Medial }; 95 96 if (lastCharShapesRight && shapingMode == SDual) { 97 if (prevForm) { 98 int correctedForm = (int) *prevForm + 1; 99 ASSERT(correctedForm >= SVGGlyphIdentifier::None && correctedForm <= SVGGlyphIdentifier::Medial); 100 *prevForm = static_cast<SVGGlyphIdentifier::ArabicForm>(correctedForm); 101 } 102 103 curForm = SVGGlyphIdentifier::Initial; 104 } else 105 curForm = shapingMode == SNone ? SVGGlyphIdentifier::None : SVGGlyphIdentifier::Isolated; 106 107 lastCharShapesRight = shapingMode != SNone; 108 return curForm; 109 } 110 111 static Vector<SVGGlyphIdentifier::ArabicForm> charactersWithArabicForm(const String& input, bool rtl) 112 { 113 Vector<SVGGlyphIdentifier::ArabicForm> forms; 114 unsigned length = input.length(); 115 116 bool containsArabic = false; 117 for (unsigned i = 0; i < length; ++i) { 118 if (isArabicChar(input[i])) { 119 containsArabic = true; 120 break; 121 } 122 } 123 124 if (!containsArabic) 125 return forms; 126 127 bool lastCharShapesRight = false; 128 129 // Start identifying arabic forms 130 if (rtl) { 131 for (int i = length - 1; i >= 0; --i) 132 forms.prepend(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.first())); 133 } else { 134 for (unsigned i = 0; i < length; ++i) 135 forms.append(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.last())); 136 } 137 138 return forms; 139 } 140 141 static inline bool isCompatibleArabicForm(const SVGGlyphIdentifier& identifier, const Vector<SVGGlyphIdentifier::ArabicForm>& chars, unsigned startPosition, unsigned endPosition) 142 { 143 if (chars.isEmpty()) 144 return true; 145 146 Vector<SVGGlyphIdentifier::ArabicForm>::const_iterator it = chars.begin() + startPosition; 147 Vector<SVGGlyphIdentifier::ArabicForm>::const_iterator end = chars.begin() + endPosition; 148 149 ASSERT(end <= chars.end()); 150 for (; it != end; ++it) { 151 if (*it != static_cast<SVGGlyphIdentifier::ArabicForm>(identifier.arabicForm) && *it != SVGGlyphIdentifier::None) 152 return false; 153 } 154 155 return true; 156 } 157 158 static inline bool isCompatibleGlyph(const SVGGlyphIdentifier& identifier, bool isVerticalText, const String& language, 159 const Vector<SVGGlyphIdentifier::ArabicForm>& chars, unsigned startPosition, unsigned endPosition) 160 { 161 bool valid = true; 162 163 // Check wheter orientation if glyph fits within the request 164 switch (identifier.orientation) { 165 case SVGGlyphIdentifier::Vertical: 166 valid = isVerticalText; 167 break; 168 case SVGGlyphIdentifier::Horizontal: 169 valid = !isVerticalText; 170 break; 171 case SVGGlyphIdentifier::Both: 172 break; 173 } 174 175 if (!valid) 176 return false; 177 178 // Check wheter languages are compatible 179 if (!identifier.languages.isEmpty()) { 180 // This glyph exists only in certain languages, if we're not specifying a 181 // language on the referencing element we're unable to use this glyph. 182 if (language.isEmpty()) 183 return false; 184 185 // Split subcode from language, if existant. 186 String languagePrefix; 187 188 size_t subCodeSeparator = language.find('-'); 189 if (subCodeSeparator != notFound) 190 languagePrefix = language.left(subCodeSeparator); 191 192 Vector<String>::const_iterator it = identifier.languages.begin(); 193 Vector<String>::const_iterator end = identifier.languages.end(); 194 195 bool found = false; 196 for (; it != end; ++it) { 197 const String& cur = *it; 198 if (cur == language || cur == languagePrefix) { 199 found = true; 200 break; 201 } 202 } 203 204 if (!found) 205 return false; 206 } 207 208 // Check wheter arabic form is compatible 209 return isCompatibleArabicForm(identifier, chars, startPosition, endPosition); 210 } 211 212 static inline const SVGFontData* svgFontAndFontFaceElementForFontData(const SimpleFontData* fontData, SVGFontFaceElement*& fontFace, SVGFontElement*& font) 213 { 214 ASSERT(fontData->isCustomFont()); 215 ASSERT(fontData->isSVGFont()); 216 217 const SVGFontData* svgFontData = static_cast<const SVGFontData*>(fontData->svgFontData()); 218 219 fontFace = svgFontData->svgFontFaceElement(); 220 ASSERT(fontFace); 221 222 font = fontFace->associatedFontElement(); 223 return svgFontData; 224 } 225 226 // Helper class to walk a text run. Lookup a SVGGlyphIdentifier for each character 227 // - also respecting possibly defined ligatures - and invoke a callback for each found glyph. 228 template<typename SVGTextRunData> 229 struct SVGTextRunWalker { 230 typedef bool (*SVGTextRunWalkerCallback)(const SVGGlyphIdentifier&, SVGTextRunData&); 231 typedef void (*SVGTextRunWalkerMissingGlyphCallback)(const TextRun&, SVGTextRunData&); 232 233 SVGTextRunWalker(const SVGFontData* fontData, SVGFontElement* fontElement, SVGTextRunData& data, 234 SVGTextRunWalkerCallback callback, SVGTextRunWalkerMissingGlyphCallback missingGlyphCallback) 235 : m_fontData(fontData) 236 , m_fontElement(fontElement) 237 , m_walkerData(data) 238 , m_walkerCallback(callback) 239 , m_walkerMissingGlyphCallback(missingGlyphCallback) 240 { 241 } 242 243 void walk(const TextRun& run, bool isVerticalText, const String& language, int from, int to) 244 { 245 ASSERT(0 <= from && from <= to && to - from <= run.length()); 246 247 const String text = Font::normalizeSpaces(run.data(from), to - from); 248 Vector<SVGGlyphIdentifier::ArabicForm> chars(charactersWithArabicForm(text, run.rtl())); 249 250 SVGGlyphIdentifier identifier; 251 bool foundGlyph = false; 252 int characterLookupRange; 253 int endOfScanRange = to + m_walkerData.extraCharsAvailable; 254 255 bool haveAltGlyph = false; 256 SVGGlyphIdentifier altGlyphIdentifier; 257 if (RenderObject* renderObject = run.referencingRenderObject()) { 258 RenderObject* parentRenderer = renderObject->parent(); 259 ASSERT(parentRenderer); 260 if (parentRenderer->node() && parentRenderer->node()->hasTagName(SVGNames::altGlyphTag)) { 261 SVGGlyphElement* glyphElement = static_cast<SVGAltGlyphElement*>(parentRenderer->node())->glyphElement(); 262 if (glyphElement) { 263 haveAltGlyph = true; 264 altGlyphIdentifier = glyphElement->buildGlyphIdentifier(); 265 altGlyphIdentifier.isValid = true; 266 altGlyphIdentifier.nameLength = to - from; 267 } 268 } 269 } 270 271 for (int i = from; i < to; ++i) { 272 // If characterLookupRange is > 0, then the font defined ligatures (length of unicode property value > 1). 273 // We have to check wheter the current character & the next character define a ligature. This needs to be 274 // extended to the n-th next character (where n is 'characterLookupRange'), to check for any possible ligature. 275 characterLookupRange = endOfScanRange - i; 276 277 String lookupString = Font::normalizeSpaces(run.data(i), characterLookupRange); 278 279 Vector<SVGGlyphIdentifier> glyphs; 280 if (haveAltGlyph) 281 glyphs.append(altGlyphIdentifier); 282 else 283 m_fontElement->getGlyphIdentifiersForString(lookupString, glyphs); 284 285 Vector<SVGGlyphIdentifier>::iterator it = glyphs.begin(); 286 Vector<SVGGlyphIdentifier>::iterator end = glyphs.end(); 287 288 for (; it != end; ++it) { 289 identifier = *it; 290 if (identifier.isValid && isCompatibleGlyph(identifier, isVerticalText, language, chars, i, i + identifier.nameLength)) { 291 ASSERT(characterLookupRange > 0); 292 i += identifier.nameLength - 1; 293 m_walkerData.charsConsumed += identifier.nameLength; 294 m_walkerData.glyphName = identifier.glyphName; 295 296 foundGlyph = true; 297 SVGGlyphElement::inheritUnspecifiedAttributes(identifier, m_fontData); 298 break; 299 } 300 } 301 302 if (!foundGlyph) { 303 ++m_walkerData.charsConsumed; 304 if (SVGMissingGlyphElement* element = m_fontElement->firstMissingGlyphElement()) { 305 // <missing-glyph> element support 306 identifier = SVGGlyphElement::buildGenericGlyphIdentifier(element); 307 SVGGlyphElement::inheritUnspecifiedAttributes(identifier, m_fontData); 308 identifier.isValid = true; 309 } else { 310 // Fallback to system font fallback 311 TextRun subRun(run); 312 subRun.setText(subRun.data(i), 1); 313 314 (*m_walkerMissingGlyphCallback)(subRun, m_walkerData); 315 continue; 316 } 317 } 318 319 if (!(*m_walkerCallback)(identifier, m_walkerData)) 320 break; 321 322 foundGlyph = false; 323 } 324 } 325 326 private: 327 const SVGFontData* m_fontData; 328 SVGFontElement* m_fontElement; 329 SVGTextRunData& m_walkerData; 330 SVGTextRunWalkerCallback m_walkerCallback; 331 SVGTextRunWalkerMissingGlyphCallback m_walkerMissingGlyphCallback; 332 }; 333 334 // Callback & data structures to compute the width of text using SVG Fonts 335 struct SVGTextRunWalkerMeasuredLengthData { 336 int at; 337 int from; 338 int to; 339 int extraCharsAvailable; 340 int charsConsumed; 341 String glyphName; 342 343 float scale; 344 float length; 345 const Font* font; 346 }; 347 348 static bool floatWidthUsingSVGFontCallback(const SVGGlyphIdentifier& identifier, SVGTextRunWalkerMeasuredLengthData& data) 349 { 350 if (data.at >= data.from && data.at < data.to) 351 data.length += identifier.horizontalAdvanceX * data.scale; 352 353 data.at++; 354 return data.at < data.to; 355 } 356 357 static void floatWidthMissingGlyphCallback(const TextRun& run, SVGTextRunWalkerMeasuredLengthData& data) 358 { 359 // Handle system font fallback 360 FontDescription fontDescription(data.font->fontDescription()); 361 fontDescription.setFamily(FontFamily()); 362 Font font(fontDescription, 0, 0); // spacing handled by SVG text code. 363 font.update(data.font->fontSelector()); 364 365 data.length += font.width(run); 366 } 367 368 369 SVGFontElement* Font::svgFont() const 370 { 371 if (!isSVGFont()) 372 return 0; 373 374 SVGFontElement* fontElement = 0; 375 SVGFontFaceElement* fontFaceElement = 0; 376 if (svgFontAndFontFaceElementForFontData(primaryFont(), fontFaceElement, fontElement)) 377 return fontElement; 378 379 return 0; 380 } 381 382 static float floatWidthOfSubStringUsingSVGFont(const Font* font, const TextRun& run, int extraCharsAvailable, int from, int to, int& charsConsumed, String& glyphName) 383 { 384 int newFrom = to > from ? from : to; 385 int newTo = to > from ? to : from; 386 387 from = newFrom; 388 to = newTo; 389 390 SVGFontElement* fontElement = 0; 391 SVGFontFaceElement* fontFaceElement = 0; 392 393 if (const SVGFontData* fontData = svgFontAndFontFaceElementForFontData(font->primaryFont(), fontFaceElement, fontElement)) { 394 if (!fontElement) 395 return 0.0f; 396 397 SVGTextRunWalkerMeasuredLengthData data; 398 399 data.font = font; 400 data.at = from; 401 data.from = from; 402 data.to = to; 403 data.extraCharsAvailable = extraCharsAvailable; 404 data.charsConsumed = 0; 405 data.scale = convertEmUnitToPixel(font->size(), fontFaceElement->unitsPerEm(), 1.0f); 406 data.length = 0.0f; 407 408 String language; 409 bool isVerticalText = false; // Holds true for HTML text 410 411 // TODO: language matching & svg glyphs should be possible for HTML text, too. 412 if (RenderObject* renderObject = run.referencingRenderObject()) { 413 RenderObject* parentRenderer = renderObject->parent(); 414 ASSERT(parentRenderer); 415 isVerticalText = isVerticalWritingMode(parentRenderer->style()->svgStyle()); 416 417 if (SVGElement* element = static_cast<SVGElement*>(parentRenderer->node())) 418 language = element->getAttribute(XMLNames::langAttr); 419 } 420 421 SVGTextRunWalker<SVGTextRunWalkerMeasuredLengthData> runWalker(fontData, fontElement, data, floatWidthUsingSVGFontCallback, floatWidthMissingGlyphCallback); 422 runWalker.walk(run, isVerticalText, language, from, to); 423 charsConsumed = data.charsConsumed; 424 glyphName = data.glyphName; 425 return data.length; 426 } 427 428 return 0.0f; 429 } 430 431 float Font::floatWidthUsingSVGFont(const TextRun& run) const 432 { 433 int charsConsumed; 434 String glyphName; 435 return floatWidthOfSubStringUsingSVGFont(this, run, 0, 0, run.length(), charsConsumed, glyphName); 436 } 437 438 float Font::floatWidthUsingSVGFont(const TextRun& run, int extraCharsAvailable, int& charsConsumed, String& glyphName) const 439 { 440 return floatWidthOfSubStringUsingSVGFont(this, run, extraCharsAvailable, 0, run.length(), charsConsumed, glyphName); 441 } 442 443 // Callback & data structures to draw text using SVG Fonts 444 struct SVGTextRunWalkerDrawTextData { 445 int extraCharsAvailable; 446 int charsConsumed; 447 String glyphName; 448 Vector<SVGGlyphIdentifier> glyphIdentifiers; 449 Vector<UChar> fallbackCharacters; 450 }; 451 452 static bool drawTextUsingSVGFontCallback(const SVGGlyphIdentifier& identifier, SVGTextRunWalkerDrawTextData& data) 453 { 454 data.glyphIdentifiers.append(identifier); 455 return true; 456 } 457 458 static void drawTextMissingGlyphCallback(const TextRun& run, SVGTextRunWalkerDrawTextData& data) 459 { 460 ASSERT(run.length() == 1); 461 data.glyphIdentifiers.append(SVGGlyphIdentifier()); 462 data.fallbackCharacters.append(run[0]); 463 } 464 465 void Font::drawTextUsingSVGFont(GraphicsContext* context, const TextRun& run, 466 const FloatPoint& point, int from, int to) const 467 { 468 SVGFontElement* fontElement = 0; 469 SVGFontFaceElement* fontFaceElement = 0; 470 471 if (const SVGFontData* fontData = svgFontAndFontFaceElementForFontData(primaryFont(), fontFaceElement, fontElement)) { 472 if (!fontElement) 473 return; 474 475 SVGTextRunWalkerDrawTextData data; 476 FloatPoint currentPoint = point; 477 float scale = convertEmUnitToPixel(size(), fontFaceElement->unitsPerEm(), 1.0f); 478 479 RenderSVGResource* activePaintingResource = run.activePaintingResource(); 480 481 // If renderObject is not set, we're dealing for HTML text rendered using SVG Fonts. 482 if (!run.referencingRenderObject()) { 483 ASSERT(!activePaintingResource); 484 485 // TODO: We're only supporting simple filled HTML text so far. 486 RenderSVGResourceSolidColor* solidPaintingResource = RenderSVGResource::sharedSolidPaintingResource(); 487 solidPaintingResource->setColor(context->fillColor()); 488 489 activePaintingResource = solidPaintingResource; 490 } 491 492 ASSERT(activePaintingResource); 493 494 int charsConsumed; 495 String glyphName; 496 bool isVerticalText = false; 497 float xStartOffset = floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName); 498 FloatPoint glyphOrigin; 499 500 String language; 501 502 // TODO: language matching & svg glyphs should be possible for HTML text, too. 503 RenderObject* referencingRenderObject = run.referencingRenderObject(); 504 RenderObject* referencingRenderObjectParent = referencingRenderObject ? referencingRenderObject->parent() : 0; 505 RenderStyle* referencingRenderObjectParentStyle = 0; 506 if (referencingRenderObject) { 507 ASSERT(referencingRenderObjectParent); 508 referencingRenderObjectParentStyle = referencingRenderObjectParent->style(); 509 510 isVerticalText = isVerticalWritingMode(referencingRenderObjectParentStyle->svgStyle()); 511 if (SVGElement* element = static_cast<SVGElement*>(referencingRenderObjectParent->node())) 512 language = element->getAttribute(XMLNames::langAttr); 513 } 514 515 if (!isVerticalText) { 516 glyphOrigin.setX(fontData->horizontalOriginX() * scale); 517 glyphOrigin.setY(fontData->horizontalOriginY() * scale); 518 } 519 520 data.extraCharsAvailable = 0; 521 data.charsConsumed = 0; 522 523 SVGTextRunWalker<SVGTextRunWalkerDrawTextData> runWalker(fontData, fontElement, data, drawTextUsingSVGFontCallback, drawTextMissingGlyphCallback); 524 runWalker.walk(run, isVerticalText, language, from, to); 525 526 RenderSVGResourceMode resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode; 527 528 unsigned numGlyphs = data.glyphIdentifiers.size(); 529 unsigned fallbackCharacterIndex = 0; 530 for (unsigned i = 0; i < numGlyphs; ++i) { 531 const SVGGlyphIdentifier& identifier = data.glyphIdentifiers[run.rtl() ? numGlyphs - i - 1 : i]; 532 if (identifier.isValid) { 533 // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). 534 if (!identifier.pathData.isEmpty()) { 535 context->save(); 536 537 if (isVerticalText) { 538 glyphOrigin.setX(identifier.verticalOriginX * scale); 539 glyphOrigin.setY(identifier.verticalOriginY * scale); 540 } 541 542 AffineTransform glyphPathTransform; 543 glyphPathTransform.translate(xStartOffset + currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); 544 glyphPathTransform.scale(scale, -scale); 545 546 Path glyphPath = identifier.pathData; 547 glyphPath.transform(glyphPathTransform); 548 549 if (activePaintingResource->applyResource(referencingRenderObjectParent, referencingRenderObjectParentStyle, context, resourceMode)) { 550 if (referencingRenderObject) { 551 RenderSVGInlineText* textRenderer = toRenderSVGInlineText(referencingRenderObject); 552 context->setStrokeThickness(context->strokeThickness() * textRenderer->scalingFactor()); 553 } 554 activePaintingResource->postApplyResource(referencingRenderObjectParent, context, resourceMode, &glyphPath); 555 } 556 557 context->restore(); 558 } 559 560 if (isVerticalText) 561 currentPoint.move(0.0f, identifier.verticalAdvanceY * scale); 562 else 563 currentPoint.move(identifier.horizontalAdvanceX * scale, 0.0f); 564 } else { 565 // Handle system font fallback 566 FontDescription fontDescription(m_fontDescription); 567 fontDescription.setFamily(FontFamily()); 568 Font font(fontDescription, 0, 0); // spacing handled by SVG text code. 569 font.update(fontSelector()); 570 571 TextRun fallbackCharacterRun(run); 572 fallbackCharacterRun.setText(&data.fallbackCharacters[run.rtl() ? data.fallbackCharacters.size() - fallbackCharacterIndex - 1 : fallbackCharacterIndex], 1); 573 font.drawText(context, fallbackCharacterRun, currentPoint); 574 575 if (isVerticalText) 576 currentPoint.move(0.0f, font.width(fallbackCharacterRun)); 577 else 578 currentPoint.move(font.width(fallbackCharacterRun), 0.0f); 579 580 fallbackCharacterIndex++; 581 } 582 } 583 } 584 } 585 586 FloatRect Font::selectionRectForTextUsingSVGFont(const TextRun& run, const FloatPoint& point, int height, int from, int to) const 587 { 588 int charsConsumed; 589 String glyphName; 590 591 return FloatRect(point.x() + floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName), 592 point.y(), floatWidthOfSubStringUsingSVGFont(this, run, 0, from, to, charsConsumed, glyphName), height); 593 } 594 595 int Font::offsetForPositionForTextUsingSVGFont(const TextRun&, float, bool) const 596 { 597 // TODO: Fix text selection when HTML text is drawn using a SVG Font 598 // We need to integrate the SVG text selection code in the offsetForPosition() framework. 599 // This will also fix a major issue, that SVG Text code can't select arabic strings properly. 600 return 0; 601 } 602 603 } 604 605 #endif 606