SVGTextRunRenderingContext.cpp [plain text]
#include "config.h"
#if ENABLE(SVG_FONTS)
#include "SVGTextRunRenderingContext.h"
#include "GlyphBuffer.h"
#include "GraphicsContext.h"
#include "RenderObject.h"
#include "RenderSVGInlineText.h"
#include "RenderSVGResourceSolidColor.h"
#include "SVGFontData.h"
#include "SVGFontElement.h"
#include "SVGFontFaceElement.h"
#include "SVGGlyphElement.h"
#include "SVGNames.h"
#include "WidthIterator.h"
namespace WebCore {
static inline const SVGFontData* svgFontAndFontFaceElementForFontData(const Font* font, SVGFontFaceElement*& svgFontFaceElement, SVGFontElement*& svgFontElement)
{
ASSERT(font);
ASSERT(font->isCustomFont());
ASSERT(font->isSVGFont());
auto* svgFontData = static_cast<const SVGFontData*>(font->svgData());
svgFontFaceElement = svgFontData->svgFontFaceElement();
ASSERT(svgFontFaceElement);
svgFontElement = svgFontFaceElement->associatedFontElement();
return svgFontData;
}
float SVGTextRunRenderingContext::floatWidthUsingSVGFont(const FontCascade& font, const TextRun& run, int& charsConsumed, String& glyphName) const
{
WidthIterator it(&font, run);
GlyphBuffer glyphBuffer;
charsConsumed += it.advance(run.length(), &glyphBuffer);
glyphName = it.lastGlyphName();
return it.runWidthSoFar();
}
bool SVGTextRunRenderingContext::applySVGKerning(const Font* font, WidthIterator& iterator, GlyphBuffer* glyphBuffer, int from) const
{
ASSERT(glyphBuffer);
ASSERT(glyphBuffer->size() > 1);
SVGFontElement* fontElement = 0;
SVGFontFaceElement* fontFaceElement = 0;
svgFontAndFontFaceElementForFontData(font, fontFaceElement, fontElement);
if (!fontElement || !fontFaceElement)
return false;
if (fontElement->horizontalKerningMapIsEmpty())
return true;
float scale = scaleEmToUnits(font->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) {
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;
}
class SVGGlyphToPathTranslator final : public GlyphToPathTranslator {
public:
SVGGlyphToPathTranslator(const TextRun* const, const GlyphBuffer&, const FloatPoint&, const SVGFontData&, SVGFontElement&, const int from, const int numGlyphs, float scale, bool isVerticalText);
private:
virtual bool containsMorePaths() override
{
return m_index != m_stoppingPoint;
}
virtual Path path() override;
virtual std::pair<float, float> extents() override;
virtual GlyphUnderlineType underlineType() override;
virtual void advance() override;
void moveToNextValidGlyph();
AffineTransform transform();
const TextRun* const m_textRun;
const GlyphBuffer& m_glyphBuffer;
const SVGFontData& m_svgFontData;
FloatPoint m_currentPoint;
FloatPoint m_glyphOrigin;
SVGGlyph m_svgGlyph;
int m_index;
Glyph m_glyph;
SVGFontElement& m_fontElement;
const float m_stoppingPoint;
const float m_scale;
const bool m_isVerticalText;
};
SVGGlyphToPathTranslator::SVGGlyphToPathTranslator(const TextRun* const textRun, const GlyphBuffer& glyphBuffer, const FloatPoint& point, const SVGFontData& svgFontData, SVGFontElement& fontElement, const int from, const int numGlyphs, float scale, bool isVerticalText)
: m_textRun(textRun)
, m_glyphBuffer(glyphBuffer)
, m_svgFontData(svgFontData)
, m_currentPoint(point)
, m_glyphOrigin(m_svgFontData.horizontalOriginX() * scale, m_svgFontData.horizontalOriginY() * scale)
, m_index(from)
, m_glyph(glyphBuffer.glyphAt(m_index))
, m_fontElement(fontElement)
, m_stoppingPoint(numGlyphs + from)
, m_scale(scale)
, m_isVerticalText(isVerticalText)
{
ASSERT(glyphBuffer.size() > m_index);
if (m_glyph) {
m_svgGlyph = m_fontElement.svgGlyphForGlyph(m_glyph);
ASSERT(!m_svgGlyph.isPartOfLigature);
ASSERT(m_svgGlyph.tableEntry == m_glyph);
SVGGlyphElement::inheritUnspecifiedAttributes(m_svgGlyph, &m_svgFontData);
}
moveToNextValidGlyph();
}
AffineTransform SVGGlyphToPathTranslator::transform()
{
AffineTransform glyphPathTransform;
glyphPathTransform.translate(m_currentPoint.x() + m_glyphOrigin.x(), m_currentPoint.y() + m_glyphOrigin.y());
glyphPathTransform.scale(m_scale, -m_scale);
return glyphPathTransform;
}
Path SVGGlyphToPathTranslator::path()
{
Path glyphPath = m_svgGlyph.pathData;
glyphPath.transform(transform());
return glyphPath;
}
std::pair<float, float> SVGGlyphToPathTranslator::extents()
{
AffineTransform glyphPathTransform = transform();
FloatPoint beginning = glyphPathTransform.mapPoint(m_currentPoint);
FloatSize end = glyphPathTransform.mapSize(FloatSize(m_glyphBuffer.advanceAt(m_index)));
return std::make_pair(beginning.x(), beginning.x() + end.width());
}
auto SVGGlyphToPathTranslator::underlineType() -> GlyphUnderlineType
{
ASSERT(m_textRun);
return computeUnderlineType(*m_textRun, m_glyphBuffer, m_index);
}
void SVGGlyphToPathTranslator::moveToNextValidGlyph()
{
if (m_glyph && !m_svgGlyph.pathData.isEmpty())
return;
advance();
}
void SVGGlyphToPathTranslator::advance()
{
do {
if (m_glyph) {
float advance = m_glyphBuffer.advanceAt(m_index).width();
if (m_isVerticalText)
m_currentPoint.move(0, advance);
else
m_currentPoint.move(advance, 0);
}
++m_index;
if (m_index >= m_stoppingPoint || !m_glyphBuffer.fontAt(m_index)->isSVGFont())
break;
m_glyph = m_glyphBuffer.glyphAt(m_index);
if (!m_glyph)
continue;
m_svgGlyph = m_fontElement.svgGlyphForGlyph(m_glyph);
ASSERT(!m_svgGlyph.isPartOfLigature);
ASSERT(m_svgGlyph.tableEntry == m_glyph);
SVGGlyphElement::inheritUnspecifiedAttributes(m_svgGlyph, &m_svgFontData);
} while ((!m_glyph || m_svgGlyph.pathData.isEmpty()) && m_index < m_stoppingPoint);
if (containsMorePaths() && m_isVerticalText) {
m_glyphOrigin.setX(m_svgGlyph.verticalOriginX * m_scale);
m_glyphOrigin.setY(m_svgGlyph.verticalOriginY * m_scale);
}
}
class DummyGlyphToPathTranslator final : public GlyphToPathTranslator {
virtual bool containsMorePaths() override
{
return false;
}
virtual Path path() override
{
return Path();
}
virtual std::pair<float, float> extents() override
{
return std::make_pair(0.f, 0.f);
}
virtual GlyphUnderlineType underlineType() override
{
return GlyphUnderlineType::DrawOverGlyph;
}
virtual void advance() override
{
}
};
std::unique_ptr<GlyphToPathTranslator> SVGTextRunRenderingContext::createGlyphToPathTranslator(const Font& font, const TextRun* textRun, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
{
SVGFontElement* fontElement = nullptr;
SVGFontFaceElement* fontFaceElement = nullptr;
const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(&font, fontFaceElement, fontElement);
if (!fontElement || !fontFaceElement)
return std::make_unique<DummyGlyphToPathTranslator>();
auto& elementRenderer = is<RenderElement>(renderer()) ? downcast<RenderElement>(renderer()) : *renderer().parent();
RenderStyle& style = elementRenderer.style();
bool isVerticalText = style.svgStyle().isVerticalWritingMode();
float scale = scaleEmToUnits(font.platformData().size(), fontFaceElement->unitsPerEm());
return std::make_unique<SVGGlyphToPathTranslator>(textRun, glyphBuffer, point, *svgFontData, *fontElement, from, numGlyphs, scale, isVerticalText);
}
void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const Font* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
{
auto activePaintingResource = this->activePaintingResource();
if (!activePaintingResource) {
RenderSVGResourceSolidColor* solidPaintingResource = RenderSVGResource::sharedSolidPaintingResource();
solidPaintingResource->setColor(context->fillColor());
activePaintingResource = solidPaintingResource;
}
auto& elementRenderer = is<RenderElement>(renderer()) ? downcast<RenderElement>(renderer()) : *renderer().parent();
RenderStyle& style = elementRenderer.style();
ASSERT(activePaintingResource);
RenderSVGResourceMode resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode;
for (auto translator = createGlyphToPathTranslator(*font, nullptr, glyphBuffer, from, numGlyphs, point); translator->containsMorePaths(); translator->advance()) {
Path glyphPath = translator->path();
if (activePaintingResource->applyResource(elementRenderer, style, context, resourceMode)) {
float strokeThickness = context->strokeThickness();
if (is<RenderSVGInlineText>(renderer()))
context->setStrokeThickness(strokeThickness * downcast<RenderSVGInlineText>(renderer()).scalingFactor());
activePaintingResource->postApplyResource(elementRenderer, context, resourceMode, &glyphPath, nullptr);
context->setStrokeThickness(strokeThickness);
}
}
}
static GlyphData missingGlyphForFont(const FontCascade& font)
{
const Font& primaryFont = font.primaryFont();
if (!primaryFont.isSVGFont())
return GlyphData();
SVGFontElement* fontElement;
SVGFontFaceElement* fontFaceElement;
svgFontAndFontFaceElementForFontData(&primaryFont, fontFaceElement, fontElement);
return GlyphData(fontElement->missingGlyph(), &primaryFont);
}
GlyphData SVGTextRunRenderingContext::glyphDataForCharacter(const FontCascade& font, WidthIterator& iterator, UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength, String& normalizedSpacesStringCache)
{
GlyphData glyphData = font.glyphDataForCharacter(character, mirror, AutoVariant);
if (!glyphData.glyph)
return missingGlyphForFont(font);
ASSERT(glyphData.font);
if (!glyphData.font->isSVGFont()) {
auto& elementRenderer = is<RenderElement>(renderer()) ? downcast<RenderElement>(renderer()) : *renderer().parent();
if (Element* parentRendererElement = elementRenderer.element()) {
if (is<SVGAltGlyphElement>(*parentRendererElement))
glyphData.font = &font.primaryFont();
}
}
if (!glyphData.font->isSVGFont())
return glyphData;
SVGFontElement* fontElement = nullptr;
SVGFontFaceElement* fontFaceElement = nullptr;
const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(glyphData.font, fontFaceElement, fontElement);
if (!svgFontData)
return glyphData;
if (svgFontData->applySVGGlyphSelection(iterator, glyphData, mirror, currentCharacter, advanceLength, normalizedSpacesStringCache))
return glyphData;
GlyphData missingGlyphData = missingGlyphForFont(font);
if (missingGlyphData.glyph)
return missingGlyphData;
FontDescription fallbackDescription = font.fontDescription();
fallbackDescription.setFamilies(Vector<AtomicString> { sansSerifFamily });
FontCascade fallbackFont(fallbackDescription, font.letterSpacing(), font.wordSpacing());
fallbackFont.update(font.fontSelector());
return fallbackFont.glyphDataForCharacter(character, mirror, AutoVariant);
}
}
#endif