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