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()); }
float RenderTextControlMultiLine::getAverageCharWidth() { #if !PLATFORM(IOS) // Since Lucida Grande is the default font, we want this to match the width // of Courier New, the default font for textareas in IE, Firefox and Safari Win. // 1229 is the avgCharWidth value in the OS/2 table for Courier New. if (style().font().firstFamily() == "Lucida Grande") return scaleEmToUnits(1229); #endif return RenderTextControl::getAverageCharWidth(); }
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); }
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 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); } }