KHtml

SVGFont.cpp
1 /**
2  * Copyright (C) 2007, 2008 Nikolas Zimmermann <[email protected]>
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 #if ENABLE(SVG_FONTS)
22 #include "Font.h"
23 
24 #include "CSSFontSelector.h"
25 #include "GraphicsContext.h"
26 #include "RenderObject.h"
27 #include "SimpleFontData.h"
28 #include "SVGAltGlyphElement.h"
29 #include "SVGFontData.h"
30 #include "SVGGlyphElement.h"
31 #include "SVGGlyphMap.h"
32 #include "SVGFontElement.h"
33 #include "SVGFontFaceElement.h"
34 #include "SVGMissingGlyphElement.h"
35 #include "SVGPaintServer.h"
36 #include "SVGPaintServerSolid.h"
37 #include "XMLNames.h"
38 
39 using namespace WTF::Unicode;
40 
41 namespace WebCore
42 {
43 
44 static inline float convertEmUnitToPixel(float fontSize, float unitsPerEm, float value)
45 {
46  if (unitsPerEm == 0.0f) {
47  return 0.0f;
48  }
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 
91  // Use a simple state machine to identify the actual arabic form
92  // It depends on the order of the arabic form enum:
93  // enum ArabicForm { None = 0, Isolated, Terminal, Initial, Medial };
94 
95  if (lastCharShapesRight && shapingMode == SDual) {
96  if (prevForm) {
97  int correctedForm = (int) * prevForm + 1;
98  ASSERT(correctedForm >= SVGGlyphIdentifier::None && correctedForm <= SVGGlyphIdentifier::Medial);
99  *prevForm = static_cast<SVGGlyphIdentifier::ArabicForm>(correctedForm);
100  }
101 
102  curForm = SVGGlyphIdentifier::Initial;
103  } else {
104  curForm = shapingMode == SNone ? SVGGlyphIdentifier::None : SVGGlyphIdentifier::Isolated;
105  }
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 int length = input.length();
115 
116  bool containsArabic = false;
117  for (unsigned int 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 
128  bool lastCharShapesRight = false;
129 
130  // Start identifying arabic forms
131  if (rtl)
132  for (int i = length - 1; i >= 0; --i) {
133  forms.prepend(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.first()));
134  }
135  else
136  for (unsigned int i = 0; i < length; ++i) {
137  forms.append(processArabicFormDetection(input[i], lastCharShapesRight, forms.isEmpty() ? 0 : &forms.last()));
138  }
139 
140  return forms;
141 }
142 
143 static inline bool isCompatibleArabicForm(const SVGGlyphIdentifier &identifier, const Vector<SVGGlyphIdentifier::ArabicForm> &chars, unsigned int startPosition, unsigned int endPosition)
144 {
145  if (chars.isEmpty()) {
146  return true;
147  }
148 
149  Vector<SVGGlyphIdentifier::ArabicForm>::const_iterator it = chars.begin() + startPosition;
150  Vector<SVGGlyphIdentifier::ArabicForm>::const_iterator end = chars.begin() + endPosition;
151 
152  ASSERT(end <= chars.end());
153  for (; it != end; ++it) {
154  if ((*it) != identifier.arabicForm && (*it) != SVGGlyphIdentifier::None) {
155  return false;
156  }
157  }
158 
159  return true;
160 }
161 
162 static inline bool isCompatibleGlyph(const SVGGlyphIdentifier &identifier, bool isVerticalText, const String &language,
163  const Vector<SVGGlyphIdentifier::ArabicForm> &chars, unsigned int startPosition, unsigned int endPosition)
164 {
165  bool valid = true;
166 
167  // Check whether orientation if glyph fits within the request
168  switch (identifier.orientation) {
169  case SVGGlyphIdentifier::Vertical:
170  valid = isVerticalText;
171  break;
172  case SVGGlyphIdentifier::Horizontal:
173  valid = !isVerticalText;
174  break;
175  case SVGGlyphIdentifier::Both:
176  break;
177  }
178 
179  if (!valid) {
180  return false;
181  }
182 
183  // Check whether languages are compatible
184  if (!identifier.languages.isEmpty()) {
185  // This glyph exists only in certain languages, if we're not specifying a
186  // language on the referencing element we're unable to use this glyph.
187  if (language.isEmpty()) {
188  return false;
189  }
190 
191  // Split subcode from language, if existent.
192  String languagePrefix;
193 
194  int subCodeSeparator = language.find('-');
195  if (subCodeSeparator != -1) {
196  languagePrefix = language.left(subCodeSeparator);
197  }
198 
199  Vector<String>::const_iterator it = identifier.languages.begin();
200  Vector<String>::const_iterator end = identifier.languages.end();
201 
202  bool found = false;
203  for (; it != end; ++it) {
204  String cur = (*it);
205 
206  if (cur == language || cur == languagePrefix) {
207  found = true;
208  break;
209  }
210  }
211 
212  if (!found) {
213  return false;
214  }
215  }
216 
217  // Check whether arabic form is compatible
218  return isCompatibleArabicForm(identifier, chars, startPosition, endPosition);
219 }
220 
221 static inline const SVGFontData *svgFontAndFontFaceElementForFontData(const SimpleFontData *fontData, SVGFontFaceElement *&fontFace, SVGFontElement *&font)
222 {
223  ASSERT(fontData->isCustomFont());
224  ASSERT(fontData->isSVGFont());
225 
226  const SVGFontData *svgFontData = static_cast<const SVGFontData *>(fontData->svgFontData());
227 
228  fontFace = svgFontData->svgFontFaceElement();
229  ASSERT(fontFace);
230 
231  font = fontFace->associatedFontElement();
232  return svgFontData;
233 }
234 
235 // Helper class to walk a text run. Lookup a SVGGlyphIdentifier for each character
236 // - also respecting possibly defined ligatures - and invoke a callback for each found glyph.
237 template<typename SVGTextRunData>
238 struct SVGTextRunWalker {
239  typedef bool (*SVGTextRunWalkerCallback)(const SVGGlyphIdentifier &, SVGTextRunData &);
240  typedef void (*SVGTextRunWalkerMissingGlyphCallback)(const TextRun &, SVGTextRunData &);
241 
242  SVGTextRunWalker(const SVGFontData *fontData, SVGFontElement *fontElement, SVGTextRunData &data,
243  SVGTextRunWalkerCallback callback, SVGTextRunWalkerMissingGlyphCallback missingGlyphCallback)
244  : m_fontData(fontData)
245  , m_fontElement(fontElement)
246  , m_walkerData(data)
247  , m_walkerCallback(callback)
248  , m_walkerMissingGlyphCallback(missingGlyphCallback)
249  {
250  }
251 
252  void walk(const TextRun &run, bool isVerticalText, const String &language, int from, int to)
253  {
254  // Should hold true for SVG text, otherwhise sth. is wrong
255  ASSERT(to - from == run.length());
256 
257  Vector<SVGGlyphIdentifier::ArabicForm> chars(charactersWithArabicForm(String(run.data(from), run.length()), run.rtl()));
258 
259  SVGGlyphIdentifier identifier;
260  bool foundGlyph = false;
261  int characterLookupRange;
262  int endOfScanRange = to + m_walkerData.extraCharsAvailable;
263 
264  bool haveAltGlyph = false;
265  SVGGlyphIdentifier altGlyphIdentifier;
266  if (RenderObject *renderObject = run.referencingRenderObject()) {
267  if (renderObject->element() && renderObject->element()->hasTagName(SVGNames::altGlyphTag)) {
268  SVGGlyphElement *glyphElement = static_cast<SVGAltGlyphElement *>(renderObject->element())->glyphElement();
269  if (glyphElement) {
270  haveAltGlyph = true;
271  altGlyphIdentifier = glyphElement->buildGlyphIdentifier();
272  altGlyphIdentifier.isValid = true;
273  altGlyphIdentifier.nameLength = to - from;
274  }
275  }
276  }
277 
278  for (int i = from; i < to; ++i) {
279  // If characterLookupRange is > 0, then the font defined ligatures (length of unicode property value > 1).
280  // We have to check whether the current character & the next character define a ligature. This needs to be
281  // extended to the n-th next character (where n is 'characterLookupRange'), to check for any possible ligature.
282  characterLookupRange = endOfScanRange - i;
283 
284  String lookupString(run.data(i), characterLookupRange);
285  Vector<SVGGlyphIdentifier> glyphs;
286  if (haveAltGlyph) {
287  glyphs.append(altGlyphIdentifier);
288  } else {
289  m_fontElement->getGlyphIdentifiersForString(lookupString, glyphs);
290  }
291 
292  Vector<SVGGlyphIdentifier>::iterator it = glyphs.begin();
293  Vector<SVGGlyphIdentifier>::iterator end = glyphs.end();
294 
295  for (; it != end; ++it) {
296  identifier = *it;
297  if (identifier.isValid && isCompatibleGlyph(identifier, isVerticalText, language, chars, i, i + identifier.nameLength)) {
298  ASSERT(characterLookupRange > 0);
299  i += identifier.nameLength - 1;
300  m_walkerData.charsConsumed += identifier.nameLength;
301  m_walkerData.glyphName = identifier.glyphName;
302 
303  foundGlyph = true;
304  SVGGlyphElement::inheritUnspecifiedAttributes(identifier, m_fontData);
305  break;
306  }
307  }
308 
309  if (!foundGlyph) {
310  ++m_walkerData.charsConsumed;
311  if (SVGMissingGlyphElement *element = m_fontElement->firstMissingGlyphElement()) {
312  // <missing-glyph> element support
313  identifier = SVGGlyphElement::buildGenericGlyphIdentifier(element);
314  SVGGlyphElement::inheritUnspecifiedAttributes(identifier, m_fontData);
315  identifier.isValid = true;
316  } else {
317  // Fallback to system font fallback
318  TextRun subRun(run);
319  subRun.setText(subRun.data(i), 1);
320 
321  (*m_walkerMissingGlyphCallback)(subRun, m_walkerData);
322  continue;
323  }
324  }
325 
326  if (!(*m_walkerCallback)(identifier, m_walkerData)) {
327  break;
328  }
329 
330  foundGlyph = false;
331  }
332  }
333 
334 private:
335  const SVGFontData *m_fontData;
336  SVGFontElement *m_fontElement;
337  SVGTextRunData &m_walkerData;
338  SVGTextRunWalkerCallback m_walkerCallback;
339  SVGTextRunWalkerMissingGlyphCallback m_walkerMissingGlyphCallback;
340 };
341 
342 // Callback & data structures to compute the width of text using SVG Fonts
343 struct SVGTextRunWalkerMeasuredLengthData {
344  int at;
345  int from;
346  int to;
347  int extraCharsAvailable;
348  int charsConsumed;
349  String glyphName;
350 
351  float scale;
352  float length;
353  const Font *font;
354 };
355 
356 bool floatWidthUsingSVGFontCallback(const SVGGlyphIdentifier &identifier, SVGTextRunWalkerMeasuredLengthData &data)
357 {
358  if (data.at >= data.from && data.at < data.to) {
359  data.length += identifier.horizontalAdvanceX * data.scale;
360  }
361 
362  data.at++;
363  return data.at < data.to;
364 }
365 
366 void floatWidthMissingGlyphCallback(const TextRun &run, SVGTextRunWalkerMeasuredLengthData &data)
367 {
368  // Handle system font fallback
369  FontDescription fontDescription(data.font->fontDescription());
370  fontDescription.setFamily(FontFamily());
371  Font font(fontDescription, 0, 0); // spacing handled by SVG text code.
372  font.update(data.font->fontSelector());
373 
374  data.length += font.floatWidth(run);
375 }
376 
377 SVGFontElement *Font::svgFont() const
378 {
379  if (!isSVGFont()) {
380  return 0;
381  }
382 
383  SVGFontElement *fontElement = 0;
384  SVGFontFaceElement *fontFaceElement = 0;
385  if (svgFontAndFontFaceElementForFontData(primaryFont(), fontFaceElement, fontElement)) {
386  return fontElement;
387  }
388 
389  return 0;
390 }
391 
392 static float floatWidthOfSubStringUsingSVGFont(const Font *font, const TextRun &run, int extraCharsAvailable, int from, int to, int &charsConsumed, String &glyphName)
393 {
394  int newFrom = to > from ? from : to;
395  int newTo = to > from ? to : from;
396 
397  from = newFrom;
398  to = newTo;
399 
400  SVGFontElement *fontElement = 0;
401  SVGFontFaceElement *fontFaceElement = 0;
402 
403  if (const SVGFontData *fontData = svgFontAndFontFaceElementForFontData(font->primaryFont(), fontFaceElement, fontElement)) {
404  if (!fontElement) {
405  return 0.0f;
406  }
407 
408  SVGTextRunWalkerMeasuredLengthData data;
409 
410  data.font = font;
411  data.at = from;
412  data.from = from;
413  data.to = to;
414  data.extraCharsAvailable = extraCharsAvailable;
415  data.charsConsumed = 0;
416  data.scale = convertEmUnitToPixel(font->size(), fontFaceElement->unitsPerEm(), 1.0f);
417  data.length = 0.0f;
418 
419  String language;
420  bool isVerticalText = false; // Holds true for HTML text
421 
422  // TODO: language matching & svg glyphs should be possible for HTML text, too.
423  if (RenderObject *renderObject = run.referencingRenderObject()) {
424  isVerticalText = isVerticalWritingMode(renderObject->style()->svgStyle());
425 
426  if (SVGElement *element = static_cast<SVGElement *>(renderObject->element())) {
427  language = element->getAttribute(XMLNames::langAttr);
428  }
429  }
430 
431  SVGTextRunWalker<SVGTextRunWalkerMeasuredLengthData> runWalker(fontData, fontElement, data, floatWidthUsingSVGFontCallback, floatWidthMissingGlyphCallback);
432  runWalker.walk(run, isVerticalText, language, 0, run.length());
433  charsConsumed = data.charsConsumed;
434  glyphName = data.glyphName;
435  return data.length;
436  }
437 
438  return 0.0f;
439 }
440 
441 float Font::floatWidthUsingSVGFont(const TextRun &run) const
442 {
443  int charsConsumed;
444  String glyphName;
445  return floatWidthOfSubStringUsingSVGFont(this, run, 0, 0, run.length(), charsConsumed, glyphName);
446 }
447 
448 float Font::floatWidthUsingSVGFont(const TextRun &run, int extraCharsAvailable, int &charsConsumed, String &glyphName) const
449 {
450  return floatWidthOfSubStringUsingSVGFont(this, run, extraCharsAvailable, 0, run.length(), charsConsumed, glyphName);
451 }
452 
453 // Callback & data structures to draw text using SVG Fonts
454 struct SVGTextRunWalkerDrawTextData {
455  int extraCharsAvailable;
456  int charsConsumed;
457  String glyphName;
458  Vector<SVGGlyphIdentifier> glyphIdentifiers;
459  Vector<UChar> fallbackCharacters;
460 };
461 
462 bool drawTextUsingSVGFontCallback(const SVGGlyphIdentifier &identifier, SVGTextRunWalkerDrawTextData &data)
463 {
464  data.glyphIdentifiers.append(identifier);
465  return true;
466 }
467 
468 void drawTextMissingGlyphCallback(const TextRun &run, SVGTextRunWalkerDrawTextData &data)
469 {
470  ASSERT(run.length() == 1);
471  data.glyphIdentifiers.append(SVGGlyphIdentifier());
472  data.fallbackCharacters.append(run[0]);
473 }
474 
475 void Font::drawTextUsingSVGFont(GraphicsContext *context, const TextRun &run,
476  const FloatPoint &point, int from, int to) const
477 {
478  SVGFontElement *fontElement = 0;
479  SVGFontFaceElement *fontFaceElement = 0;
480 
481  if (const SVGFontData *fontData = svgFontAndFontFaceElementForFontData(primaryFont(), fontFaceElement, fontElement)) {
482  if (!fontElement) {
483  return;
484  }
485 
486  SVGTextRunWalkerDrawTextData data;
487  FloatPoint currentPoint = point;
488  float scale = convertEmUnitToPixel(size(), fontFaceElement->unitsPerEm(), 1.0f);
489 
490  SVGPaintServer *activePaintServer = run.activePaintServer();
491 
492  // If renderObject is not set, we're dealing for HTML text rendered using SVG Fonts.
493  if (!run.referencingRenderObject()) {
494  ASSERT(!activePaintServer);
495 
496  // TODO: We're only supporting simple filled HTML text so far.
497  SVGPaintServerSolid *solidPaintServer = SVGPaintServer::sharedSolidPaintServer();
498  solidPaintServer->setColor(context->fillColor());
499 
500  activePaintServer = solidPaintServer;
501  }
502 
503  ASSERT(activePaintServer);
504 
505  int charsConsumed;
506  String glyphName;
507  bool isVerticalText = false;
508  float xStartOffset = floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName);
509  FloatPoint glyphOrigin;
510 
511  String language;
512 
513  // TODO: language matching & svg glyphs should be possible for HTML text, too.
514  if (run.referencingRenderObject()) {
515  isVerticalText = isVerticalWritingMode(run.referencingRenderObject()->style()->svgStyle());
516 
517  if (SVGElement *element = static_cast<SVGElement *>(run.referencingRenderObject()->element())) {
518  language = element->getAttribute(XMLNames::langAttr);
519  }
520  }
521 
522  if (!isVerticalText) {
523  glyphOrigin.setX(fontData->horizontalOriginX() * scale);
524  glyphOrigin.setY(fontData->horizontalOriginY() * scale);
525  }
526 
527  data.extraCharsAvailable = 0;
528 
529  SVGTextRunWalker<SVGTextRunWalkerDrawTextData> runWalker(fontData, fontElement, data, drawTextUsingSVGFontCallback, drawTextMissingGlyphCallback);
530  runWalker.walk(run, isVerticalText, language, from, to);
531 
532  SVGPaintTargetType targetType = context->textDrawingMode() == cTextStroke ? ApplyToStrokeTargetType : ApplyToFillTargetType;
533 
534  unsigned numGlyphs = data.glyphIdentifiers.size();
535  unsigned fallbackCharacterIndex = 0;
536  for (unsigned i = 0; i < numGlyphs; ++i) {
537  const SVGGlyphIdentifier &identifier = data.glyphIdentifiers[run.rtl() ? numGlyphs - i - 1 : i];
538  if (identifier.isValid) {
539  // FIXME: Support arbitrary SVG content as glyph (currently limited to <glyph d="..."> situations).
540  if (!identifier.pathData.isEmpty()) {
541  context->save();
542 
543  if (isVerticalText) {
544  glyphOrigin.setX(identifier.verticalOriginX * scale);
545  glyphOrigin.setY(identifier.verticalOriginY * scale);
546  }
547 
548  context->translate(xStartOffset + currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y());
549  context->scale(FloatSize(scale, -scale));
550 
551  context->beginPath();
552  context->addPath(identifier.pathData);
553 
554  if (activePaintServer->setup(context, run.referencingRenderObject(), targetType)) {
555  // Spec: Any properties specified on a text elements which represents a length, such as the
556  // 'stroke-width' property, might produce surprising results since the length value will be
557  // processed in the coordinate system of the glyph. (TODO: What other lengths? miter-limit? dash-offset?)
558  if (targetType == ApplyToStrokeTargetType && scale != 0.0f) {
559  context->setStrokeThickness(context->strokeThickness() / scale);
560  }
561 
562  activePaintServer->renderPath(context, run.referencingRenderObject(), targetType);
563  activePaintServer->teardown(context, run.referencingRenderObject(), targetType);
564  }
565 
566  context->restore();
567  }
568 
569  if (isVerticalText) {
570  currentPoint.move(0.0f, identifier.verticalAdvanceY * scale);
571  } else {
572  currentPoint.move(identifier.horizontalAdvanceX * scale, 0.0f);
573  }
574  } else {
575  // Handle system font fallback
576  FontDescription fontDescription(context->font().fontDescription());
577  fontDescription.setFamily(FontFamily());
578  Font font(fontDescription, 0, 0); // spacing handled by SVG text code.
579  font.update(context->font().fontSelector());
580 
581  TextRun fallbackCharacterRun(run);
582  fallbackCharacterRun.setText(&data.fallbackCharacters[run.rtl() ? data.fallbackCharacters.size() - fallbackCharacterIndex - 1 : fallbackCharacterIndex], 1);
583  font.drawText(context, fallbackCharacterRun, currentPoint);
584 
585  if (isVerticalText) {
586  currentPoint.move(0.0f, font.floatWidth(fallbackCharacterRun));
587  } else {
588  currentPoint.move(font.floatWidth(fallbackCharacterRun), 0.0f);
589  }
590 
591  fallbackCharacterIndex++;
592  }
593  }
594  }
595 }
596 
597 FloatRect Font::selectionRectForTextUsingSVGFont(const TextRun &run, const IntPoint &point, int height, int from, int to) const
598 {
599  int charsConsumed;
600  String glyphName;
601 
602  return FloatRect(point.x() + floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName),
603  point.y(), floatWidthOfSubStringUsingSVGFont(this, run, 0, from, to, charsConsumed, glyphName), height);
604 }
605 
606 int Font::offsetForPositionForTextUsingSVGFont(const TextRun &, int position, bool includePartialGlyphs) const
607 {
608  // TODO: Fix text selection when HTML text is drawn using a SVG Font
609  // We need to integrate the SVG text selection code in the offsetForPosition() framework.
610  // This will also fix a major issue, that SVG Text code can't select arabic strings properly.
611  return 0;
612 }
613 
614 }
615 
616 #endif
const QList< QKeySequence > & end()
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:10 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.