SimpleFontData::SimpleFontData(PassOwnPtr<SVGFontData> svgFontData, int size, bool syntheticBold, bool syntheticItalic) : m_platformData(FontPlatformData(size, syntheticBold, syntheticItalic)) , m_treatAsFixedPitch(false) , m_svgFontData(svgFontData) , m_isCustomFont(true) , m_isLoading(false) , m_isTextOrientationFallback(false) , m_isBrokenIdeographFallback(false) , m_hasVerticalGlyphs(false) { SVGFontFaceElement* svgFontFaceElement = m_svgFontData->svgFontFaceElement(); unsigned unitsPerEm = svgFontFaceElement->unitsPerEm(); float scale = size; if (unitsPerEm) scale /= unitsPerEm; float xHeight = svgFontFaceElement->xHeight() * scale; float ascent = svgFontFaceElement->ascent() * scale; float descent = svgFontFaceElement->descent() * scale; float lineGap = 0.1f * size; SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement(); if (!xHeight) { // Fallback if x_heightAttr is not specified for the font element. Vector<SVGGlyphIdentifier> letterXGlyphs; associatedFontElement->getGlyphIdentifiersForString(String("x", 1), letterXGlyphs); xHeight = letterXGlyphs.isEmpty() ? 2 * ascent / 3 : letterXGlyphs.first().horizontalAdvanceX * scale; } m_fontMetrics.setUnitsPerEm(unitsPerEm); m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setLineSpacing(roundf(ascent) + roundf(descent) + roundf(lineGap)); m_fontMetrics.setXHeight(xHeight); Vector<SVGGlyphIdentifier> spaceGlyphs; associatedFontElement->getGlyphIdentifiersForString(String(" ", 1), spaceGlyphs); m_spaceWidth = spaceGlyphs.isEmpty() ? xHeight : spaceGlyphs.first().horizontalAdvanceX * scale; Vector<SVGGlyphIdentifier> numeralZeroGlyphs; associatedFontElement->getGlyphIdentifiersForString(String("0", 1), numeralZeroGlyphs); m_avgCharWidth = numeralZeroGlyphs.isEmpty() ? m_spaceWidth : numeralZeroGlyphs.first().horizontalAdvanceX * scale; Vector<SVGGlyphIdentifier> letterWGlyphs; associatedFontElement->getGlyphIdentifiersForString(String("W", 1), letterWGlyphs); m_maxCharWidth = letterWGlyphs.isEmpty() ? ascent : letterWGlyphs.first().horizontalAdvanceX * scale; // FIXME: is there a way we can get the space glyph from the SVGGlyphIdentifier above? m_spaceGlyph = 0; m_zeroWidthSpaceGlyph = 0; determinePitch(); m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; }
float SVGFontData::widthForSVGGlyph(Glyph glyph, float fontSize) const { SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement(); ASSERT(svgFontFaceElement); SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement(); ASSERT(associatedFontElement); SVGGlyph svgGlyph = associatedFontElement->svgGlyphForGlyph(glyph); SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, this); return svgGlyph.horizontalAdvanceX * scaleEmToUnits(fontSize, svgFontFaceElement->unitsPerEm()); }
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; }
std::unique_ptr<GlyphToPathTranslator> SVGTextRunRenderingContext::createGlyphToPathTranslator(const SimpleFontData& fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(&fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return std::make_unique<DummyGlyphToPathTranslator>(); auto& elementRenderer = renderer().isRenderElement() ? toRenderElement(renderer()) : *renderer().parent(); RenderStyle& style = elementRenderer.style(); bool isVerticalText = style.svgStyle().isVerticalWritingMode(); float scale = scaleEmToUnits(fontData.platformData().size(), fontFaceElement->unitsPerEm()); return std::make_unique<SVGGlyphToPathTranslator>(glyphBuffer, point, *svgFontData, *fontElement, from, numGlyphs, scale, isVerticalText); }
SimpleFontData::SimpleFontData(PassOwnPtr<SVGFontData> svgFontData, int size, bool syntheticBold, bool syntheticItalic) : m_orientation(Horizontal) , m_platformData(FontPlatformData(size, syntheticBold, syntheticItalic)) , m_treatAsFixedPitch(false) , m_svgFontData(svgFontData) , m_isCustomFont(true) , m_isLoading(false) , m_isBrokenIdeographFont(false) { SVGFontFaceElement* svgFontFaceElement = m_svgFontData->svgFontFaceElement(); m_unitsPerEm = svgFontFaceElement->unitsPerEm(); double scale = size; if (m_unitsPerEm) scale /= m_unitsPerEm; m_ascent = static_cast<int>(svgFontFaceElement->ascent() * scale); m_descent = static_cast<int>(svgFontFaceElement->descent() * scale); m_xHeight = static_cast<int>(svgFontFaceElement->xHeight() * scale); m_lineGap = 0.1f * size; m_lineSpacing = m_ascent + m_descent + m_lineGap; SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement(); Vector<SVGGlyphIdentifier> spaceGlyphs; associatedFontElement->getGlyphIdentifiersForString(String(" ", 1), spaceGlyphs); m_spaceWidth = spaceGlyphs.isEmpty() ? m_xHeight : static_cast<float>(spaceGlyphs.first().horizontalAdvanceX * scale); Vector<SVGGlyphIdentifier> numeralZeroGlyphs; associatedFontElement->getGlyphIdentifiersForString(String("0", 1), numeralZeroGlyphs); m_avgCharWidth = numeralZeroGlyphs.isEmpty() ? m_spaceWidth : static_cast<float>(numeralZeroGlyphs.first().horizontalAdvanceX * scale); Vector<SVGGlyphIdentifier> letterWGlyphs; associatedFontElement->getGlyphIdentifiersForString(String("W", 1), letterWGlyphs); m_maxCharWidth = letterWGlyphs.isEmpty() ? m_ascent : static_cast<float>(letterWGlyphs.first().horizontalAdvanceX * scale); // FIXME: is there a way we can get the space glyph from the SVGGlyphIdentifier above? m_spaceGlyph = 0; m_zeroWidthSpaceGlyph = 0; determinePitch(); m_adjustedSpaceWidth = roundf(m_spaceWidth); m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; }
bool SVGTextRunRenderingContext::applySVGKerning(const SimpleFontData* fontData, WidthIterator& iterator, GlyphBuffer* glyphBuffer, int from) const { ASSERT(glyphBuffer); ASSERT(glyphBuffer->size() > 1); SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return false; if (fontElement->horizontalKerningMapIsEmpty()) return true; float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm()); String lastGlyphName; String lastUnicodeString; int characterOffset = iterator.m_currentCharacter; String text = iterator.run().string(); const int glyphCount = glyphBuffer->size() - from; GlyphBufferAdvance* advances = glyphBuffer->advances(from); for (int i = 0; i < glyphCount; ++i) { Glyph glyph = glyphBuffer->glyphAt(from + i); if (!glyph) continue; float kerning = 0; SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph); String unicodeString = text.substring(characterOffset, svgGlyph.unicodeStringLength); if (i >= 1) { // FIXME: Support vertical text. kerning = fontElement->horizontalKerningForPairOfStringsAndGlyphs(lastUnicodeString, lastGlyphName, unicodeString, svgGlyph.glyphName); advances[i - 1].setWidth(advances[i - 1].width() - kerning * scale); } lastGlyphName = svgGlyph.glyphName; lastUnicodeString = unicodeString; characterOffset += svgGlyph.unicodeStringLength; } return true; }
void SVGFontData::initializeFontData(SimpleFontData* fontData, float fontSize) { ASSERT(fontData); SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement(); ASSERT(svgFontFaceElement); SVGFontElement* svgFontElement = svgFontFaceElement->associatedFontElement(); ASSERT(svgFontElement); GlyphData missingGlyphData; missingGlyphData.fontData = fontData; missingGlyphData.glyph = svgFontElement->missingGlyph(); fontData->setMissingGlyphData(missingGlyphData); fontData->setZeroWidthSpaceGlyph(0); fontData->determinePitch(); unsigned unitsPerEm = svgFontFaceElement->unitsPerEm(); float scale = scaleEmToUnits(fontSize, unitsPerEm); float xHeight = svgFontFaceElement->xHeight() * scale; float ascent = svgFontFaceElement->ascent() * scale; float descent = svgFontFaceElement->descent() * scale; float lineGap = 0.1f * fontSize; GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(fontData, 0)->page(); if (!xHeight && glyphPageZero) { // Fallback if x_heightAttr is not specified for the font element. Glyph letterXGlyph = glyphPageZero->glyphDataForCharacter('x').glyph; xHeight = letterXGlyph ? fontData->widthForGlyph(letterXGlyph) : 2 * ascent / 3; } FontMetrics& fontMetrics = fontData->fontMetrics(); fontMetrics.setUnitsPerEm(unitsPerEm); fontMetrics.setAscent(ascent); fontMetrics.setDescent(descent); fontMetrics.setLineGap(lineGap); fontMetrics.setLineSpacing(roundf(ascent) + roundf(descent) + roundf(lineGap)); fontMetrics.setXHeight(xHeight); if (!glyphPageZero) { fontData->setSpaceGlyph(0); fontData->setSpaceWidths(0); fontData->setAvgCharWidth(0); fontData->setMaxCharWidth(ascent); return; } // Calculate space width. Glyph spaceGlyph = glyphPageZero->glyphDataForCharacter(' ').glyph; fontData->setSpaceGlyph(spaceGlyph); fontData->setSpaceWidths(fontData->widthForGlyph(spaceGlyph)); // Estimate average character width. Glyph numeralZeroGlyph = glyphPageZero->glyphDataForCharacter('0').glyph; fontData->setAvgCharWidth(numeralZeroGlyph ? fontData->widthForGlyph(numeralZeroGlyph) : fontData->spaceWidth()); // Estimate maximum character width. Glyph letterWGlyph = glyphPageZero->glyphDataForCharacter('W').glyph; fontData->setMaxCharWidth(letterWGlyph ? fontData->widthForGlyph(letterWGlyph) : ascent); }
void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return; // We can only paint SVGFonts if a context is available. RenderSVGResource* activePaintingResource = activePaintingResourceFromRun(run); RenderObject* renderObject = renderObjectFromRun(run); RenderObject* parentRenderObject = firstParentRendererForNonTextNode(renderObject); RenderStyle* parentRenderObjectStyle = 0; ASSERT(renderObject); if (!activePaintingResource) { // TODO: We're only supporting simple filled HTML text so far. RenderSVGResourceSolidColor* solidPaintingResource = RenderSVGResource::sharedSolidPaintingResource(); solidPaintingResource->setColor(context->fillColor()); activePaintingResource = solidPaintingResource; } bool isVerticalText = false; if (parentRenderObject) { parentRenderObjectStyle = parentRenderObject->style(); ASSERT(parentRenderObjectStyle); isVerticalText = parentRenderObjectStyle->svgStyle()->isVerticalWritingMode(); } float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm()); ASSERT(activePaintingResource); FloatPoint glyphOrigin; glyphOrigin.setX(svgFontData->horizontalOriginX() * scale); glyphOrigin.setY(svgFontData->horizontalOriginY() * scale); FloatPoint currentPoint = point; RenderSVGResourceMode resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode; for (int i = 0; i < numGlyphs; ++i) { Glyph glyph = glyphBuffer.glyphAt(from + i); if (!glyph) continue; float advance = glyphBuffer.advanceAt(from + i); SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph); ASSERT(!svgGlyph.isPartOfLigature); ASSERT(svgGlyph.tableEntry == glyph); SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData); // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). if (svgGlyph.pathData.isEmpty()) { if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); continue; } context->save(); if (isVerticalText) { glyphOrigin.setX(svgGlyph.verticalOriginX * scale); glyphOrigin.setY(svgGlyph.verticalOriginY * scale); } AffineTransform glyphPathTransform; glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); glyphPathTransform.scale(scale, -scale); Path glyphPath = svgGlyph.pathData; glyphPath.transform(glyphPathTransform); if (activePaintingResource->applyResource(parentRenderObject, parentRenderObjectStyle, context, resourceMode)) { if (renderObject && renderObject->isSVGInlineText()) { const RenderSVGInlineText* textRenderer = toRenderSVGInlineText(renderObject); context->setStrokeThickness(context->strokeThickness() * textRenderer->scalingFactor()); } activePaintingResource->postApplyResource(parentRenderObject, context, resourceMode, &glyphPath, 0); } context->restore(); if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); } }
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 SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return; // We can only paint SVGFonts if a context is available. RenderObject* renderObject = renderObjectFromRun(run); ASSERT(renderObject); bool isVerticalText = false; if (RenderObject* parentRenderObject = firstParentRendererForNonTextNode(renderObject)) { RenderStyle* parentRenderObjectStyle = parentRenderObject->style(); ASSERT(parentRenderObjectStyle); isVerticalText = parentRenderObjectStyle->svgStyle().isVerticalWritingMode(); } float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm()); FloatPoint glyphOrigin; glyphOrigin.setX(svgFontData->horizontalOriginX() * scale); glyphOrigin.setY(svgFontData->horizontalOriginY() * scale); unsigned short resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode; FloatPoint currentPoint = point; for (int i = 0; i < numGlyphs; ++i) { Glyph glyph = glyphBuffer.glyphAt(from + i); if (!glyph) continue; float advance = glyphBuffer.advanceAt(from + i); SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph); ASSERT(!svgGlyph.isPartOfLigature); ASSERT(svgGlyph.tableEntry == glyph); SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData); // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations). if (svgGlyph.pathData.isEmpty()) { if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); continue; } if (isVerticalText) { glyphOrigin.setX(svgGlyph.verticalOriginX * scale); glyphOrigin.setY(svgGlyph.verticalOriginY * scale); } AffineTransform glyphPathTransform; glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y()); glyphPathTransform.scale(scale, -scale); Path glyphPath = svgGlyph.pathData; glyphPath.transform(glyphPathTransform); SVGRenderSupport::fillOrStrokePath(context, resourceMode, glyphPath); if (isVerticalText) currentPoint.move(0, advance); else currentPoint.move(advance, 0); } }