7dd60d49eece6015117d301cd1ef98043af2c019
[WebKit-https.git] / Source / WebCore / rendering / svg / SVGTextRunRenderingContext.cpp
1 /*
2  * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22
23 #if ENABLE(SVG_FONTS)
24 #include "SVGTextRunRenderingContext.h"
25
26 #include "Font.h"
27 #include "GlyphBuffer.h"
28 #include "GraphicsContext.h"
29 #include "RenderObject.h"
30 #include "RenderSVGInlineText.h"
31 #include "RenderSVGResourceSolidColor.h"
32 #include "SVGFontData.h"
33 #include "SVGFontElement.h"
34 #include "SVGFontFaceElement.h"
35 #include "SVGGlyphElement.h"
36 #include "SVGNames.h"
37 #include "WidthIterator.h"
38
39 namespace WebCore {
40
41 static inline const SVGFontData* svgFontAndFontFaceElementForFontData(const SimpleFontData* fontData, SVGFontFaceElement*& fontFace, SVGFontElement*& font)
42 {
43     ASSERT(fontData);
44     ASSERT(fontData->isCustomFont());
45     ASSERT(fontData->isSVGFont());
46
47     const SVGFontData* svgFontData = static_cast<const SVGFontData*>(fontData->fontData());
48
49     fontFace = svgFontData->svgFontFaceElement();
50     ASSERT(fontFace);
51
52     font = fontFace->associatedFontElement();
53     return svgFontData;
54 }
55
56 static inline RenderObject* firstParentRendererForNonTextNode(RenderObject* renderer)
57 {
58     ASSERT(renderer);
59     return renderer->isText() ? renderer->parent() : renderer;
60 }
61
62 static inline RenderObject* renderObjectFromRun(const TextRun& run)
63 {
64     if (TextRun::RenderingContext* renderingContext = run.renderingContext())
65         return static_cast<SVGTextRunRenderingContext*>(renderingContext)->renderer();
66     return 0;
67 }
68
69 static inline RenderSVGResource* activePaintingResourceFromRun(const TextRun& run)
70 {
71     if (TextRun::RenderingContext* renderingContext = run.renderingContext())
72         return static_cast<SVGTextRunRenderingContext*>(renderingContext)->activePaintingResource();
73     return 0;
74 }
75
76 float SVGTextRunRenderingContext::floatWidthUsingSVGFont(const Font& font, const TextRun& run, int& charsConsumed, String& glyphName) const
77 {
78     WidthIterator it(&font, run);
79     GlyphBuffer glyphBuffer;
80     charsConsumed += it.advance(run.length(), &glyphBuffer);
81     glyphName = it.lastGlyphName();
82     return it.runWidthSoFar();
83 }
84  
85 void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
86 {
87     SVGFontElement* fontElement = 0;
88     SVGFontFaceElement* fontFaceElement = 0;
89
90     const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement);
91     if (!fontElement || !fontFaceElement)
92         return;
93
94     // We can only paint SVGFonts if a context is available.
95     RenderSVGResource* activePaintingResource = activePaintingResourceFromRun(run);
96     RenderObject* renderObject = renderObjectFromRun(run);
97     RenderObject* parentRenderObject = firstParentRendererForNonTextNode(renderObject);
98     RenderStyle* parentRenderObjectStyle = 0;
99
100     ASSERT(renderObject);
101     if (!activePaintingResource) {
102         // TODO: We're only supporting simple filled HTML text so far.
103         RenderSVGResourceSolidColor* solidPaintingResource = RenderSVGResource::sharedSolidPaintingResource();
104         solidPaintingResource->setColor(context->fillColor());
105         activePaintingResource = solidPaintingResource;
106     }
107  
108     bool isVerticalText = false;
109     if (parentRenderObject) {
110         parentRenderObjectStyle = parentRenderObject->style();
111         ASSERT(parentRenderObjectStyle);
112         isVerticalText = parentRenderObjectStyle->svgStyle()->isVerticalWritingMode();
113     }
114
115     float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm());
116     ASSERT(activePaintingResource);
117
118     FloatPoint glyphOrigin;
119     glyphOrigin.setX(svgFontData->horizontalOriginX() * scale);
120     glyphOrigin.setY(svgFontData->horizontalOriginY() * scale);
121
122     FloatPoint currentPoint = point;
123     RenderSVGResourceMode resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode;
124     for (int i = 0; i < numGlyphs; ++i) {
125         Glyph glyph = glyphBuffer.glyphAt(from + i);
126         if (!glyph)
127             continue;
128
129         float advance = glyphBuffer.advanceAt(from + i);
130         SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph);
131         ASSERT(!svgGlyph.isPartOfLigature);
132         ASSERT(svgGlyph.tableEntry == glyph);
133
134         SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData);
135
136         // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations).
137         if (svgGlyph.pathData.isEmpty()) {
138             if (isVerticalText)
139                 currentPoint.move(0, advance);
140             else
141                 currentPoint.move(advance, 0);
142             continue;
143          }
144
145         if (isVerticalText) {
146             glyphOrigin.setX(svgGlyph.verticalOriginX * scale);
147             glyphOrigin.setY(svgGlyph.verticalOriginY * scale);
148          }
149
150         AffineTransform glyphPathTransform;
151         glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y());
152         glyphPathTransform.scale(scale, -scale);
153
154         Path glyphPath = svgGlyph.pathData;
155         glyphPath.transform(glyphPathTransform);
156
157         if (activePaintingResource->applyResource(parentRenderObject, parentRenderObjectStyle, context, resourceMode)) {
158             float strokeThickness = context->strokeThickness();
159             if (renderObject && renderObject->isSVGInlineText())
160                 context->setStrokeThickness(strokeThickness * toRenderSVGInlineText(renderObject)->scalingFactor());
161             activePaintingResource->postApplyResource(parentRenderObject, context, resourceMode, &glyphPath, 0);
162             context->setStrokeThickness(strokeThickness);
163         }
164
165         if (isVerticalText)
166             currentPoint.move(0, advance);
167         else
168             currentPoint.move(advance, 0);
169     }
170 }
171
172 GlyphData SVGTextRunRenderingContext::glyphDataForCharacter(const Font& font, const TextRun& run, WidthIterator& iterator, UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength)
173 {
174     const SimpleFontData* primaryFont = font.primaryFont();
175     ASSERT(primaryFont);
176
177     pair<GlyphData, GlyphPage*> pair = font.glyphDataAndPageForCharacter(character, mirror);
178     GlyphData glyphData = pair.first;
179
180     // Check if we have the missing glyph data, in which case we can just return.
181     GlyphData missingGlyphData = primaryFont->missingGlyphData();
182     if (glyphData.glyph == missingGlyphData.glyph && glyphData.fontData == missingGlyphData.fontData) {
183         ASSERT(glyphData.fontData);
184         return glyphData;
185     }
186
187     // Save data fromt he font fallback list because we may modify it later. Do this before the
188     // potential change to glyphData.fontData below.
189     FontFallbackList* fontList = font.fontList();
190     ASSERT(fontList);
191     FontFallbackList::GlyphPagesStateSaver glyphPagesSaver(*fontList);
192
193     // Characters enclosed by an <altGlyph> element, may not be registered in the GlyphPage.
194     const SimpleFontData* originalFontData = glyphData.fontData;
195     if (glyphData.fontData && !glyphData.fontData->isSVGFont()) {
196         if (TextRun::RenderingContext* renderingContext = run.renderingContext()) {
197             RenderObject* renderObject = static_cast<SVGTextRunRenderingContext*>(renderingContext)->renderer();
198             RenderObject* parentRenderObject = renderObject->isText() ? renderObject->parent() : renderObject;
199             ASSERT(parentRenderObject);
200             if (Element* parentRenderObjectElement = toElement(parentRenderObject->node())) {
201                 if (parentRenderObjectElement->hasTagName(SVGNames::altGlyphTag))
202                     glyphData.fontData = primaryFont;
203             }
204         }
205     }
206
207     const SimpleFontData* fontData = glyphData.fontData;
208     if (fontData) {
209         if (!fontData->isSVGFont())
210             return glyphData;
211
212         SVGFontElement* fontElement = 0;
213         SVGFontFaceElement* fontFaceElement = 0;
214
215         const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement);
216         if (!fontElement || !fontFaceElement)
217             return glyphData;
218
219         // If we got here, we're dealing with a glyph defined in a SVG Font.
220         // The returned glyph by glyphDataAndPageForCharacter() is a glyph stored in the SVG Font glyph table.
221         // This doesn't necessarily mean the glyph is suitable for rendering/measuring in this context, its
222         // arabic-form/orientation/... may not match, we have to apply SVG Glyph selection to discover that.
223         if (svgFontData->applySVGGlyphSelection(iterator, glyphData, mirror, currentCharacter, advanceLength))
224             return glyphData;
225     }
226
227     GlyphPage* page = pair.second;
228     ASSERT(page);
229
230     // No suitable glyph found that is compatible with the requirments (same language, arabic-form, orientation etc.)
231     // Even though our GlyphPage contains an entry for eg. glyph "a", it's not compatible. So we have to temporarily
232     // remove the glyph data information from the GlyphPage, and retry the lookup, which handles font fallbacks correctly.
233     page->setGlyphDataForCharacter(character, glyphData.glyph, 0);
234
235     // Assure that the font fallback glyph selection worked, aka. the fallbackGlyphData font data is not the same as before.
236     GlyphData fallbackGlyphData = font.glyphDataForCharacter(character, mirror);
237     ASSERT(fallbackGlyphData.fontData != fontData);
238
239     // Restore original state of the SVG Font glyph table and the current font fallback list,
240     // to assure the next lookup of the same glyph won't immediately return the fallback glyph.
241     page->setGlyphDataForCharacter(character, glyphData.glyph, originalFontData);
242     ASSERT(fallbackGlyphData.fontData);
243     return fallbackGlyphData;
244 }
245
246 }
247
248 #endif