https://bugs.webkit.org/show_bug.cgi?id=128936
Reviewed by Darin Adler.
Source/WebCore:
When drawing glyphs in an SVG font, the glyphs are converted to paths and then filled. This patch moves
the glyph -> path conversion into a helper class, GlyphToPathTranslator, and creates an implementation
for the SVG drawing code. Once this helper class is created, it can be used to trace paths in order
to make underlines skip over SVG glyphs. This helper class also has an implementation for non-SVG glyphs,
which allows for the glyph tracing code to be paramaterized over the implementation of the helper class
rather than if the FontData itself is SVG or not.
Tests: fast/css3-text/css3-text-decoration/text-decoration-skip/text-decoration-skip-ink-svg.html
* platform/graphics/Font.h:
(WebCore::GlyphToPathTranslator::~GlyphToPathTranslator): Virtual implementation of helper class
* platform/graphics/cg/PathCG.cpp:
(WebCore::Path::Path): Created constructor that takes a RefPtr<CGMutablePathRef>
* platform/graphics/Path.h:
* platform/graphics/TextRun.h: Give RenderingContext a factory function to create the helper class
instance
* platform/graphics/mac/FontMac.mm: Implementation of helper class used for skipping underlines on
regular (CoreText) glyphs
(WebCore::MacGlyphToPathTranslator::MacGlyphToPathTranslator):
(WebCore::MacGlyphToPathTranslator::moveToNextValidGlyph):
(WebCore::MacGlyphToPathTranslator::incrementIndex):
(WebCore::Font::dashesForIntersectionsWithRect): Call the relevant factory function, and use it
to successively generate Paths
* rendering/svg/SVGTextRunRenderingContext.cpp: Implementation of helper class used for SVG fonts
(WebCore::SVGGlyphToPathTranslator::SVGGlyphToPathTranslator):
(WebCore::SVGGlyphToPathTranslator::moveToNextValidGlyph):
(WebCore::SVGGlyphToPathTranslator::incrementIndex):
(WebCore::SVGTextRunRenderingContext::createGlyphToPathTranslator):
(WebCore::SVGTextRunRenderingContext::drawSVGGlyphs): Use the above implementation
* rendering/svg/SVGTextRunRenderingContext.h: Factory function declaration
LayoutTests:
This font simply draws some underlined text with a SVG font and makes sure the underline skips.
* fast/css3-text/css3-text-decoration/text-decoration-skip/resources/Litherum.svg: Added.
* fast/css3-text/css3-text-decoration/text-decoration-skip/text-decoration-skip-ink-svg-expected.html: Added.
* fast/css3-text/css3-text-decoration/text-decoration-skip/text-decoration-skip-ink-svg.html: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@164842
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2014-02-17 Myles C. Maxfield <mmaxfield@apple.com>
+
+ text-decoration-skip: ink does not skip over SVG fonts
+ https://bugs.webkit.org/show_bug.cgi?id=128936
+
+ Reviewed by Darin Adler.
+
+ This font simply draws some underlined text with a SVG font and makes sure the underline skips.
+
+ * fast/css3-text/css3-text-decoration/text-decoration-skip/resources/Litherum.svg: Added.
+ * fast/css3-text/css3-text-decoration/text-decoration-skip/text-decoration-skip-ink-svg-expected.html: Added.
+ * fast/css3-text/css3-text-decoration/text-decoration-skip/text-decoration-skip-ink-svg.html: Added.
+
2014-02-27 Thiago de Barros Lacerda <thiago.lacerda@openbossa.org>
[WebRTC] Removing MediaConstraints argument from RTCPeerConnection addStream, updateIce methods and constructor
--- /dev/null
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata></metadata>
+<defs>
+<font id="Litherum" horiz-adv-x="1024">
+<font-face units-per-em="14" ascent="14" descent="-7"/>
+<glyph unicode="|" horiz-adv-x="14" d="M5 -7v21h4v-21z"/>
+</font>
+</defs>
+</svg>
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+This test draws a giant character with an SVG font and positions the viewport
+in the gap where the underline should be skipping over the character. It should appear
+white.
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+ font-family: 'Litherum';
+ src: url("./resources/Litherum.svg") format(svg)
+}
+#p {
+ font: 1000px 'Litherum';
+ text-decoration: underline;
+}
+</style>
+</head>
+<body>
+This test draws a giant character with an SVG font and positions the viewport
+in the gap where the underline should be skipping over the character. It should appear
+white.
+<div style="position: absolute; width: 62px; height: 100px; overflow: hidden;">
+<div style="position: absolute; left: -295px; top: -1100px;">
+<div id="p">|</div>
+</div>
+</div>
+</body>
+</html>
+2014-02-17 Myles C. Maxfield <mmaxfield@apple.com>
+
+ text-decoration-skip: ink does not skip over SVG fonts
+ https://bugs.webkit.org/show_bug.cgi?id=128936
+
+ Reviewed by Darin Adler.
+
+ When drawing glyphs in an SVG font, the glyphs are converted to paths and then filled. This patch moves
+ the glyph -> path conversion into a helper class, GlyphToPathTranslator, and creates an implementation
+ for the SVG drawing code. Once this helper class is created, it can be used to trace paths in order
+ to make underlines skip over SVG glyphs. This helper class also has an implementation for non-SVG glyphs,
+ which allows for the glyph tracing code to be paramaterized over the implementation of the helper class
+ rather than if the FontData itself is SVG or not.
+
+ Tests: fast/css3-text/css3-text-decoration/text-decoration-skip/text-decoration-skip-ink-svg.html
+
+ * platform/graphics/Font.h:
+ (WebCore::GlyphToPathTranslator::~GlyphToPathTranslator): Virtual implementation of helper class
+ * platform/graphics/cg/PathCG.cpp:
+ (WebCore::Path::Path): Created constructor that takes a RefPtr<CGMutablePathRef>
+ * platform/graphics/Path.h:
+ * platform/graphics/TextRun.h: Give RenderingContext a factory function to create the helper class
+ instance
+ * platform/graphics/mac/FontMac.mm: Implementation of helper class used for skipping underlines on
+ regular (CoreText) glyphs
+ (WebCore::MacGlyphToPathTranslator::MacGlyphToPathTranslator):
+ (WebCore::MacGlyphToPathTranslator::moveToNextValidGlyph):
+ (WebCore::MacGlyphToPathTranslator::incrementIndex):
+ (WebCore::Font::dashesForIntersectionsWithRect): Call the relevant factory function, and use it
+ to successively generate Paths
+ * rendering/svg/SVGTextRunRenderingContext.cpp: Implementation of helper class used for SVG fonts
+ (WebCore::SVGGlyphToPathTranslator::SVGGlyphToPathTranslator):
+ (WebCore::SVGGlyphToPathTranslator::moveToNextValidGlyph):
+ (WebCore::SVGGlyphToPathTranslator::incrementIndex):
+ (WebCore::SVGTextRunRenderingContext::createGlyphToPathTranslator):
+ (WebCore::SVGTextRunRenderingContext::drawSVGGlyphs): Use the above implementation
+ * rendering/svg/SVGTextRunRenderingContext.h: Factory function declaration
+
2014-02-27 Thiago de Barros Lacerda <thiago.lacerda@openbossa.org>
[WebRTC] Removing MediaConstraints argument from RTCPeerConnection addStream, updateIce methods and constructor
#include "DashArray.h"
#include "FontDescription.h"
#include "FontGlyphs.h"
+#include "Path.h"
#include "SimpleFontData.h"
#include "TextDirection.h"
#include "TypesettingFeatures.h"
bool computeBounds;
};
+class GlyphToPathTranslator {
+public:
+ virtual bool containsMorePaths() = 0;
+ virtual Path nextPath() = 0;
+ virtual ~GlyphToPathTranslator() { }
+};
class Font {
public:
#include <wtf/Forward.h>
#if USE(CG)
+#include <wtf/RetainPtr.h>
+#include <CoreGraphics/CGPath.h>
typedef struct CGPath PlatformPath;
#elif USE(CAIRO)
namespace WebCore {
WTF_MAKE_FAST_ALLOCATED;
public:
Path();
+#if USE(CG)
+ Path(RetainPtr<CGMutablePathRef>);
+#endif
~Path();
Path(const Path&);
class Font;
class GraphicsContext;
class GlyphBuffer;
+class GlyphToPathTranslator;
class SimpleFontData;
struct GlyphData;
struct WidthIterator;
class RenderingContext : public RefCounted<RenderingContext> {
public:
+ virtual std::unique_ptr<GlyphToPathTranslator> createGlyphToPathTranslator(const SimpleFontData&, const GlyphBuffer&, int from, int numGlyphs, const FloatPoint&) const = 0;
virtual ~RenderingContext() { }
#if ENABLE(SVG_FONTS)
{
}
+Path::Path(RetainPtr<CGMutablePathRef> p)
+ : m_path(p.leakRef())
+{
+}
+
Path::~Path()
{
if (m_path)
state.currentPoint = point;
}
+class MacGlyphToPathTranslator final : public GlyphToPathTranslator {
+public:
+ MacGlyphToPathTranslator(const GlyphBuffer& glyphBuffer, const FloatPoint& textOrigin)
+ : m_index(0)
+ , m_glyphBuffer(glyphBuffer)
+ , m_fontData(glyphBuffer.fontDataAt(m_index))
+ , m_translation(CGAffineTransformScale(CGAffineTransformMakeTranslation(textOrigin.x(), textOrigin.y()), 1, -1))
+ {
+ moveToNextValidGlyph();
+ }
+private:
+ virtual bool containsMorePaths() override
+ {
+ return m_index != m_glyphBuffer.size();
+ }
+ virtual Path nextPath() override;
+ void moveToNextValidGlyph();
+ void incrementIndex();
+
+ int m_index;
+ const GlyphBuffer& m_glyphBuffer;
+ const SimpleFontData* m_fontData;
+ CGAffineTransform m_translation;
+};
+
+Path MacGlyphToPathTranslator::nextPath()
+{
+ RetainPtr<CGPathRef> result = adoptCF(CTFontCreatePathForGlyph(m_fontData->platformData().ctFont(), m_glyphBuffer.glyphAt(m_index), &m_translation));
+ incrementIndex();
+ return adoptCF(CGPathCreateMutableCopy(result.get()));
+}
+
+void MacGlyphToPathTranslator::moveToNextValidGlyph()
+{
+ if (!m_fontData->isSVGFont())
+ return;
+ incrementIndex();
+}
+
+void MacGlyphToPathTranslator::incrementIndex()
+{
+ do {
+ GlyphBufferAdvance advance = m_glyphBuffer.advanceAt(m_index);
+ m_translation = CGAffineTransformTranslate(m_translation, advance.width(), advance.height());
+ ++m_index;
+ if (m_index >= m_glyphBuffer.size())
+ break;
+ m_fontData = m_glyphBuffer.fontDataAt(m_index);
+ } while (m_fontData->isSVGFont() && m_index < m_glyphBuffer.size());
+}
+
DashArray Font::dashesForIntersectionsWithRect(const TextRun& run, const FloatPoint& textOrigin, const FloatRect& lineExtents) const
{
if (loadingCustomFonts())
return DashArray();
- float deltaX;
GlyphBuffer glyphBuffer;
- if (codePath(run) != Complex)
+ float deltaX;
+ if (codePath(run) != Font::Complex)
deltaX = getGlyphsAndAdvancesForSimpleText(run, 0, run.length(), glyphBuffer);
else
deltaX = getGlyphsAndAdvancesForComplexText(run, 0, run.length(), glyphBuffer);
- CGAffineTransform translation = CGAffineTransformMakeTranslation(textOrigin.x() + deltaX, textOrigin.y());
- translation = CGAffineTransformScale(translation, 1, -1);
+
+ if (!glyphBuffer.size())
+ return DashArray();
+
+ // FIXME: Handle SVG + non-SVG interleaved runs
+ const SimpleFontData* fontData = glyphBuffer.fontDataAt(0);
+ std::unique_ptr<GlyphToPathTranslator> translator;
+ bool isSVG = false;
+ FloatPoint origin = FloatPoint(textOrigin.x() + deltaX, textOrigin.y());
+ if (!fontData->isSVGFont())
+ translator = std::move(std::make_unique<MacGlyphToPathTranslator>(glyphBuffer, origin));
+ else {
+ translator = std::move(run.renderingContext()->createGlyphToPathTranslator(*fontData, glyphBuffer, 0, run.length(), origin));
+ isSVG = true;
+ }
DashArray result;
- for (int i = 0; i < glyphBuffer.size(); ++i) {
+ for (int index = 0; translator->containsMorePaths(); ++index) {
GlyphIterationState info = GlyphIterationState(CGPointMake(0, 0), CGPointMake(0, 0), lineExtents.y(), lineExtents.y() + lineExtents.height(), lineExtents.x() + lineExtents.width(), lineExtents.x());
- const SimpleFontData* fontData = glyphBuffer.fontDataAt(i);
- if (fontData->isSVGFont())
- continue;
- RetainPtr<CGPathRef> path = adoptCF(CTFontCreatePathForGlyph(fontData->platformData().ctFont(), glyphBuffer.glyphAt(i), &translation));
- CGPathApply(path.get(), &info, &findPathIntersections);
+ const SimpleFontData* localFontData = glyphBuffer.fontDataAt(index);
+ if (!localFontData || (!isSVG && localFontData->isSVGFont()) || (isSVG && localFontData != fontData))
+ break; // The advances will get all messed up if we do anything other than bail here.
+ Path path = translator->nextPath();
+ CGPathApply(path.platformPath(), &info, &findPathIntersections);
if (info.minX < info.maxX) {
result.append(info.minX - lineExtents.x());
result.append(info.maxX - lineExtents.x());
}
- GlyphBufferAdvance advance = glyphBuffer.advanceAt(i);
- translation = CGAffineTransformTranslate(translation, advance.width(), advance.height());
}
return result;
}
return true;
}
-void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
+class SVGGlyphToPathTranslator final : public GlyphToPathTranslator {
+public:
+ SVGGlyphToPathTranslator(const GlyphBuffer& glyphBuffer, const FloatPoint& point, const SVGFontData& svgFontData, SVGFontElement& fontElement, const int from, const int numGlyphs, float scale, bool isVerticalText);
+private:
+ virtual bool containsMorePaths() override
+ {
+ return m_index != m_stoppingPoint;
+ }
+ virtual Path nextPath() override;
+ void moveToNextValidGlyph();
+ void incrementIndex();
+
+ 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 GlyphBuffer& glyphBuffer, const FloatPoint& point, const SVGFontData& svgFontData, SVGFontElement& fontElement, const int from, const int numGlyphs, float scale, bool isVerticalText)
+ : 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();
+}
+
+Path SVGGlyphToPathTranslator::nextPath()
+{
+ if (m_isVerticalText) {
+ m_glyphOrigin.setX(m_svgGlyph.verticalOriginX * m_scale);
+ m_glyphOrigin.setY(m_svgGlyph.verticalOriginY * m_scale);
+ }
+
+ AffineTransform glyphPathTransform;
+ glyphPathTransform.translate(m_currentPoint.x() + m_glyphOrigin.x(), m_currentPoint.y() + m_glyphOrigin.y());
+ glyphPathTransform.scale(m_scale, -m_scale);
+
+ Path glyphPath = m_svgGlyph.pathData;
+ glyphPath.transform(glyphPathTransform);
+ incrementIndex();
+ return glyphPath;
+}
+
+void SVGGlyphToPathTranslator::moveToNextValidGlyph()
+{
+ if (m_glyph && !m_svgGlyph.pathData.isEmpty())
+ return;
+ incrementIndex();
+}
+
+void SVGGlyphToPathTranslator::incrementIndex()
+{
+ 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)
+ 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);
+}
+
+class DummyGlyphToPathTranslator final : public GlyphToPathTranslator {
+ virtual bool containsMorePaths() override
+ {
+ return false;
+ }
+ virtual Path nextPath() override
+ {
+ return Path();
+ }
+};
+
+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);
+ const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(&fontData, fontFaceElement, fontElement);
if (!fontElement || !fontFaceElement)
- return;
+ 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);
+}
+
+void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
+{
auto activePaintingResource = this->activePaintingResource();
if (!activePaintingResource) {
// TODO: We're only supporting simple filled HTML text so far.
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());
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).width();
- 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);
-
+ auto translator(createGlyphToPathTranslator(*fontData, glyphBuffer, from, numGlyphs, point));
+ while (translator->containsMorePaths()) {
+ Path glyphPath = translator->nextPath();
if (activePaintingResource->applyResource(elementRenderer, style, context, resourceMode)) {
float strokeThickness = context->strokeThickness();
if (renderer().isSVGInlineText())
activePaintingResource->postApplyResource(elementRenderer, context, resourceMode, &glyphPath, 0);
context->setStrokeThickness(strokeThickness);
}
-
- if (isVerticalText)
- currentPoint.move(0, advance);
- else
- currentPoint.move(advance, 0);
}
}
virtual ~SVGTextRunRenderingContext() { }
+ virtual std::unique_ptr<GlyphToPathTranslator> createGlyphToPathTranslator(const SimpleFontData&, const GlyphBuffer&, int from, int numGlyphs, const FloatPoint&) const override;
+
RenderObject& m_renderer;
#if ENABLE(SVG_FONTS)