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 "core/rendering/svg/SVGTextRunRenderingContext.h"
25
26 #include "SVGNames.h"
27 #include "core/rendering/RenderObject.h"
28 #include "core/rendering/svg/RenderSVGInlineText.h"
29 #include "core/rendering/svg/RenderSVGResourceSolidColor.h"
30 #include "core/svg/SVGFontData.h"
31 #include "core/svg/SVGFontElement.h"
32 #include "core/svg/SVGFontFaceElement.h"
33 #include "core/svg/SVGGlyphElement.h"
34 #include "platform/fonts/GlyphBuffer.h"
35 #include "platform/fonts/WidthIterator.h"
36 #include "platform/graphics/GraphicsContext.h"
37
38 namespace WebCore {
39
svgFontAndFontFaceElementForFontData(const SimpleFontData * fontData,SVGFontFaceElement * & fontFace,SVGFontElement * & font)40 static inline const SVGFontData* svgFontAndFontFaceElementForFontData(const SimpleFontData* fontData, SVGFontFaceElement*& fontFace, SVGFontElement*& font)
41 {
42 ASSERT(fontData);
43 ASSERT(fontData->isCustomFont());
44 ASSERT(fontData->isSVGFont());
45
46 RefPtr<CustomFontData> customFontData = fontData->customFontData();
47 const SVGFontData* svgFontData = static_cast<const SVGFontData*>(customFontData.get());
48
49 fontFace = svgFontData->svgFontFaceElement();
50 ASSERT(fontFace);
51
52 font = fontFace->associatedFontElement();
53 return svgFontData;
54 }
55
firstParentRendererForNonTextNode(RenderObject * renderer)56 static inline RenderObject* firstParentRendererForNonTextNode(RenderObject* renderer)
57 {
58 ASSERT(renderer);
59 return renderer->isText() ? renderer->parent() : renderer;
60 }
61
renderObjectFromRun(const TextRun & run)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
activePaintingResourceFromRun(const TextRun & run)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
floatWidthUsingSVGFont(const Font & font,const TextRun & run,int & charsConsumed,String & glyphName) const76 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
drawSVGGlyphs(GraphicsContext * context,const TextRun & run,const SimpleFontData * fontData,const GlyphBuffer & glyphBuffer,int from,int numGlyphs,const FloatPoint & point) const85 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
glyphDataForCharacter(const Font & font,const TextRun & run,WidthIterator & iterator,UChar32 character,bool mirror,int currentCharacter,unsigned & advanceLength)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 (originalFontData && !originalFontData->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, 0, 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
249