1 /**
2 * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann@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
convertEmUnitToPixel(float fontSize,float unitsPerEm,float value)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
isVerticalWritingMode(const SVGRenderStyle * style)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
processArabicFormDetection(const UChar & curChar,bool & lastCharShapesRight,SVGGlyphIdentifier::ArabicForm * prevForm)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
charactersWithArabicForm(const String & input,bool rtl)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
isCompatibleArabicForm(const SVGGlyphIdentifier & identifier,const Vector<SVGGlyphIdentifier::ArabicForm> & chars,unsigned startPosition,unsigned endPosition)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
isCompatibleGlyph(const SVGGlyphIdentifier & identifier,bool isVerticalText,const String & language,const Vector<SVGGlyphIdentifier::ArabicForm> & chars,unsigned startPosition,unsigned endPosition)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
svgFontAndFontFaceElementForFontData(const SimpleFontData * fontData,SVGFontFaceElement * & fontFace,SVGFontElement * & font)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
SVGTextRunWalkerWebCore::SVGTextRunWalker231 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
walkWebCore::SVGTextRunWalker241 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
floatWidthUsingSVGFontCallback(const SVGGlyphIdentifier & identifier,SVGTextRunWalkerMeasuredLengthData & data)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
floatWidthMissingGlyphCallback(const TextRun & run,SVGTextRunWalkerMeasuredLengthData & data)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
svgFont() const365 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
floatWidthOfSubStringUsingSVGFont(const Font * font,const TextRun & run,int extraCharsAvailable,int from,int to,int & charsConsumed,String & glyphName)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
floatWidthUsingSVGFont(const TextRun & run) const425 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
floatWidthUsingSVGFont(const TextRun & run,int extraCharsAvailable,int & charsConsumed,String & glyphName) const432 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
drawTextUsingSVGFontCallback(const SVGGlyphIdentifier & identifier,SVGTextRunWalkerDrawTextData & data)446 static bool drawTextUsingSVGFontCallback(const SVGGlyphIdentifier& identifier, SVGTextRunWalkerDrawTextData& data)
447 {
448 data.glyphIdentifiers.append(identifier);
449 return true;
450 }
451
drawTextMissingGlyphCallback(const TextRun & run,SVGTextRunWalkerDrawTextData & data)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
drawTextUsingSVGFont(GraphicsContext * context,const TextRun & run,const FloatPoint & point,int from,int to) const459 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
selectionRectForTextUsingSVGFont(const TextRun & run,const IntPoint & point,int height,int from,int to) const580 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
offsetForPositionForTextUsingSVGFont(const TextRun &,int,bool) const589 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