static float floatWidthOfSubStringUsingSVGFont(const Font* font, const TextRun& run, int extraCharsAvailable, int from, int to, int& charsConsumed, String& glyphName) { int newFrom = to > from ? from : to; int newTo = to > from ? to : from; from = newFrom; to = newTo; SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; if (const SVGFontData* fontData = svgFontAndFontFaceElementForFontData(font->primaryFont(), fontFaceElement, fontElement)) { if (!fontElement) return 0.0f; SVGTextRunWalkerMeasuredLengthData data; data.font = font; data.at = from; data.from = from; data.to = to; data.extraCharsAvailable = extraCharsAvailable; data.charsConsumed = 0; data.scale = convertEmUnitToPixel(font->size(), fontFaceElement->unitsPerEm(), 1.0f); data.length = 0.0f; String language; bool isVerticalText = false; // Holds true for HTML text // TODO: language matching & svg glyphs should be possible for HTML text, too. if (RenderObject* renderObject = run.referencingRenderObject()) { isVerticalText = isVerticalWritingMode(renderObject->style()->svgStyle()); if (SVGElement* element = static_cast<SVGElement*>(renderObject->element())) language = element->getAttribute(XMLNames::langAttr); } SVGTextRunWalker<SVGTextRunWalkerMeasuredLengthData> runWalker(fontData, fontElement, data, floatWidthUsingSVGFontCallback, floatWidthMissingGlyphCallback); runWalker.walk(run, isVerticalText, language, 0, run.length()); charsConsumed = data.charsConsumed; glyphName = data.glyphName; return data.length; } return 0.0f; }
void Font::drawTextUsingSVGFont(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; if (const SVGFontData* fontData = svgFontAndFontFaceElementForFontData(primaryFont(), fontFaceElement, fontElement)) { if (!fontElement) return; SVGTextRunWalkerDrawTextData data; FloatPoint currentPoint = point; float scale = convertEmUnitToPixel(size(), fontFaceElement->unitsPerEm(), 1.0f); SVGPaintServer* activePaintServer = run.activePaintServer(); // If renderObject is not set, we're dealing for HTML text rendered using SVG Fonts. if (!run.referencingRenderObject()) { ASSERT(!activePaintServer); // TODO: We're only supporting simple filled HTML text so far. SVGPaintServerSolid* solidPaintServer = SVGPaintServer::sharedSolidPaintServer(); solidPaintServer->setColor(context->fillColor()); activePaintServer = solidPaintServer; } ASSERT(activePaintServer); int charsConsumed; String glyphName; bool isVerticalText = false; float xStartOffset = floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName); FloatPoint glyphOrigin; String language; // TODO: language matching & svg glyphs should be possible for HTML text, too. if (run.referencingRenderObject()) { isVerticalText = isVerticalWritingMode(run.referencingRenderObject()->style()->svgStyle()); if (SVGElement* element = static_cast<SVGElement*>(run.referencingRenderObject()->element())) language = element->getAttribute(XMLNames::langAttr); } if (!isVerticalText) { glyphOrigin.setX(fontData->horizontalOriginX() * scale); glyphOrigin.setY(fontData->horizontalOriginY() * scale); } data.extraCharsAvailable = 0; data.charsConsumed = 0; SVGTextRunWalker<SVGTextRunWalkerDrawTextData> runWalker(fontData, fontElement, data, drawTextUsingSVGFontCallback, drawTextMissingGlyphCallback); runWalker.walk(run, isVerticalText, language, from, to); SVGPaintTargetType targetType = context->textDrawingMode() == cTextStroke ? ApplyToStrokeTargetType : ApplyToFillTargetType; unsigned numGlyphs = data.glyphIdentifiers.size(); unsigned fallbackCharacterIndex = 0; for (unsigned i = 0; i < numGlyphs; ++i) { const SVGGlyphIdentifier& identifier = data.glyphIdentifiers[run.rtl() ? numGlyphs - i - 1 : i]; if (identifier.isValid) { // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). if (!identifier.pathData.isEmpty()) { context->save(); if (isVerticalText) { glyphOrigin.setX(identifier.verticalOriginX * scale); glyphOrigin.setY(identifier.verticalOriginY * scale); } context->translate(xStartOffset + currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); context->scale(FloatSize(scale, -scale)); context->beginPath(); context->addPath(identifier.pathData); if (activePaintServer->setup(context, run.referencingRenderObject(), targetType)) { // Spec: Any properties specified on a text elements which represents a length, such as the // 'stroke-width' property, might produce surprising results since the length value will be // processed in the coordinate system of the glyph. (TODO: What other lengths? miter-limit? dash-offset?) if (targetType == ApplyToStrokeTargetType && scale != 0.0f) context->setStrokeThickness(context->strokeThickness() / scale); activePaintServer->renderPath(context, run.referencingRenderObject(), targetType); activePaintServer->teardown(context, run.referencingRenderObject(), targetType); } context->restore(); } if (isVerticalText) currentPoint.move(0.0f, identifier.verticalAdvanceY * scale); else currentPoint.move(identifier.horizontalAdvanceX * scale, 0.0f); } else { // Handle system font fallback FontDescription fontDescription(context->font().fontDescription()); fontDescription.setFamily(FontFamily()); Font font(fontDescription, 0, 0); // spacing handled by SVG text code. font.update(context->font().fontSelector()); TextRun fallbackCharacterRun(run); fallbackCharacterRun.setText(&data.fallbackCharacters[run.rtl() ? data.fallbackCharacters.size() - fallbackCharacterIndex - 1 : fallbackCharacterIndex], 1); font.drawText(context, fallbackCharacterRun, currentPoint); if (isVerticalText) currentPoint.move(0.0f, font.floatWidth(fallbackCharacterRun)); else currentPoint.move(font.floatWidth(fallbackCharacterRun), 0.0f); fallbackCharacterIndex++; } } } }
void walk(const TextRun& run, bool isVerticalText, const String& language, int from, int to) { // Should hold true for SVG text, otherwhise sth. is wrong ASSERT(to - from == run.length()); Vector<SVGGlyphIdentifier::ArabicForm> chars(charactersWithArabicForm(String(run.data(from), run.length()), run.rtl())); SVGGlyphIdentifier identifier; bool foundGlyph = false; int characterLookupRange; int endOfScanRange = to + m_walkerData.extraCharsAvailable; bool haveAltGlyph = false; SVGGlyphIdentifier altGlyphIdentifier; if (RenderObject* renderObject = run.referencingRenderObject()) { if (renderObject->element() && renderObject->element()->hasTagName(SVGNames::altGlyphTag)) { SVGGlyphElement* glyphElement = static_cast<SVGAltGlyphElement*>(renderObject->element())->glyphElement(); if (glyphElement) { haveAltGlyph = true; altGlyphIdentifier = glyphElement->buildGlyphIdentifier(); altGlyphIdentifier.isValid = true; altGlyphIdentifier.nameLength = to - from; } } } for (int i = from; i < to; ++i) { // If characterLookupRange is > 0, then the font defined ligatures (length of unicode property value > 1). // We have to check wheter the current character & the next character define a ligature. This needs to be // extended to the n-th next character (where n is 'characterLookupRange'), to check for any possible ligature. characterLookupRange = endOfScanRange - i; String lookupString(run.data(i), characterLookupRange); Vector<SVGGlyphIdentifier> glyphs; if (haveAltGlyph) glyphs.append(altGlyphIdentifier); else m_fontElement->getGlyphIdentifiersForString(lookupString, glyphs); Vector<SVGGlyphIdentifier>::iterator it = glyphs.begin(); Vector<SVGGlyphIdentifier>::iterator end = glyphs.end(); for (; it != end; ++it) { identifier = *it; if (identifier.isValid && isCompatibleGlyph(identifier, isVerticalText, language, chars, i, i + identifier.nameLength)) { ASSERT(characterLookupRange > 0); i += identifier.nameLength - 1; m_walkerData.charsConsumed += identifier.nameLength; m_walkerData.glyphName = identifier.glyphName; foundGlyph = true; SVGGlyphElement::inheritUnspecifiedAttributes(identifier, m_fontData); break; } } if (!foundGlyph) { ++m_walkerData.charsConsumed; if (SVGMissingGlyphElement* element = m_fontElement->firstMissingGlyphElement()) { // <missing-glyph> element support identifier = SVGGlyphElement::buildGenericGlyphIdentifier(element); SVGGlyphElement::inheritUnspecifiedAttributes(identifier, m_fontData); identifier.isValid = true; } else { // Fallback to system font fallback TextRun subRun(run); subRun.setText(subRun.data(i), 1); (*m_walkerMissingGlyphCallback)(subRun, m_walkerData); continue; } } if (!(*m_walkerCallback)(identifier, m_walkerData)) break; foundGlyph = false; } }