2 * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
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.
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.
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.
24 #include "SVGTextRunRenderingContext.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"
37 #include "WidthIterator.h"
41 static inline const SVGFontData* svgFontAndFontFaceElementForFontData(const SimpleFontData* fontData, SVGFontFaceElement*& fontFace, SVGFontElement*& font)
44 ASSERT(fontData->isCustomFont());
45 ASSERT(fontData->isSVGFont());
47 const SVGFontData* svgFontData = static_cast<const SVGFontData*>(fontData->fontData());
49 fontFace = svgFontData->svgFontFaceElement();
52 font = fontFace->associatedFontElement();
56 static inline RenderObject* firstParentRendererForNonTextNode(RenderObject* renderer)
59 return renderer->isText() ? renderer->parent() : renderer;
62 static inline RenderObject* renderObjectFromRun(const TextRun& run)
64 if (TextRun::RenderingContext* renderingContext = run.renderingContext())
65 return static_cast<SVGTextRunRenderingContext*>(renderingContext)->renderer();
69 static inline RenderSVGResource* activePaintingResourceFromRun(const TextRun& run)
71 if (TextRun::RenderingContext* renderingContext = run.renderingContext())
72 return static_cast<SVGTextRunRenderingContext*>(renderingContext)->activePaintingResource();
76 float SVGTextRunRenderingContext::floatWidthUsingSVGFont(const Font& font, const TextRun& run, int& charsConsumed, String& glyphName) const
78 WidthIterator it(&font, run);
79 charsConsumed += it.advance(run.length());
80 glyphName = it.lastGlyphName();
81 return it.runWidthSoFar();
84 void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
86 SVGFontElement* fontElement = 0;
87 SVGFontFaceElement* fontFaceElement = 0;
89 const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement);
90 if (!fontElement || !fontFaceElement)
93 // We can only paint SVGFonts if a context is available.
94 RenderSVGResource* activePaintingResource = activePaintingResourceFromRun(run);
95 RenderObject* renderObject = renderObjectFromRun(run);
96 RenderObject* parentRenderObject = firstParentRendererForNonTextNode(renderObject);
97 RenderStyle* parentRenderObjectStyle = 0;
100 if (!activePaintingResource) {
101 // TODO: We're only supporting simple filled HTML text so far.
102 RenderSVGResourceSolidColor* solidPaintingResource = RenderSVGResource::sharedSolidPaintingResource();
103 solidPaintingResource->setColor(context->fillColor());
104 activePaintingResource = solidPaintingResource;
107 bool isVerticalText = false;
108 if (parentRenderObject) {
109 parentRenderObjectStyle = parentRenderObject->style();
110 ASSERT(parentRenderObjectStyle);
111 isVerticalText = parentRenderObjectStyle->svgStyle()->isVerticalWritingMode();
114 float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm());
115 ASSERT(activePaintingResource);
117 FloatPoint glyphOrigin;
118 glyphOrigin.setX(svgFontData->horizontalOriginX() * scale);
119 glyphOrigin.setY(svgFontData->horizontalOriginY() * scale);
121 FloatPoint currentPoint = point;
122 RenderSVGResourceMode resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode;
123 for (int i = 0; i < numGlyphs; ++i) {
124 Glyph glyph = glyphBuffer.glyphAt(from + i);
128 float advance = glyphBuffer.advanceAt(from + i);
129 SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph);
130 ASSERT(!svgGlyph.isPartOfLigature);
131 ASSERT(svgGlyph.tableEntry == glyph);
133 SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData);
135 // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations).
136 if (svgGlyph.pathData.isEmpty()) {
138 currentPoint.move(0, advance);
140 currentPoint.move(advance, 0);
146 if (isVerticalText) {
147 glyphOrigin.setX(svgGlyph.verticalOriginX * scale);
148 glyphOrigin.setY(svgGlyph.verticalOriginY * scale);
151 AffineTransform glyphPathTransform;
152 glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y());
153 glyphPathTransform.scale(scale, -scale);
155 Path glyphPath = svgGlyph.pathData;
156 glyphPath.transform(glyphPathTransform);
158 if (activePaintingResource->applyResource(parentRenderObject, parentRenderObjectStyle, context, resourceMode)) {
159 if (renderObject && renderObject->isSVGInlineText()) {
160 const RenderSVGInlineText* textRenderer = toRenderSVGInlineText(renderObject);
161 context->setStrokeThickness(context->strokeThickness() * textRenderer->scalingFactor());
163 activePaintingResource->postApplyResource(parentRenderObject, context, resourceMode, &glyphPath, 0);
169 currentPoint.move(0, advance);
171 currentPoint.move(advance, 0);
175 GlyphData SVGTextRunRenderingContext::glyphDataForCharacter(const Font& font, const TextRun& run, WidthIterator& iterator, UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength)
177 const SimpleFontData* primaryFont = font.primaryFont();
180 pair<GlyphData, GlyphPage*> pair = font.glyphDataAndPageForCharacter(character, mirror);
181 GlyphData glyphData = pair.first;
182 if (!glyphData.fontData)
185 GlyphData missingGlyphData = primaryFont->missingGlyphData();
186 if (glyphData.glyph == missingGlyphData.glyph && glyphData.fontData == missingGlyphData.fontData)
189 // Characters enclosed by an <altGlyph> element, may not be registered in the GlyphPage.
190 if (!glyphData.fontData->isSVGFont()) {
191 if (TextRun::RenderingContext* renderingContext = run.renderingContext()) {
192 RenderObject* renderObject = static_cast<SVGTextRunRenderingContext*>(renderingContext)->renderer();
193 RenderObject* parentRenderObject = renderObject->isText() ? renderObject->parent() : renderObject;
194 ASSERT(parentRenderObject);
195 if (Element* parentRenderObjectElement = toElement(parentRenderObject->node())) {
196 if (parentRenderObjectElement->hasTagName(SVGNames::altGlyphTag))
197 glyphData.fontData = primaryFont;
202 if (!glyphData.fontData || !glyphData.fontData->isSVGFont())
205 const SimpleFontData* fontData = glyphData.fontData;
207 SVGFontElement* fontElement = 0;
208 SVGFontFaceElement* fontFaceElement = 0;
210 const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement);
211 if (!fontElement || !fontFaceElement)
214 // If we got here, we're dealing with a glyph defined in a SVG Font.
215 // The returned glyph by glyphDataAndPageForCharacter() is a glyph stored in the SVG Font glyph table.
216 // This doesn't necessarily mean the glyph is suitable for rendering/measuring in this context, its
217 // arabic-form/orientation/... may not match, we have to apply SVG Glyph selection to discover that.
218 if (svgFontData->applySVGGlyphSelection(iterator, glyphData, mirror, currentCharacter, advanceLength))
221 GlyphPage* page = pair.second;
224 FontFallbackList* fontList = font.fontList();
227 // No suitable glyph found that is compatible with the requirments (same language, arabic-form, orientation etc.)
228 // Even though our GlyphPage contains an entry for eg. glyph "a", it's not compatible. So we have to temporarily
229 // remove the glyph data information from the GlyphPage, and retry the lookup, which handles font fallbacks correctly.
230 GlyphPageTreeNode* originalGlyphPageZero = fontList->glyphPageZero();
231 const FontFallbackList::GlyphPages& originalGlyphPages = fontList->glyphPages();
232 page->setGlyphDataForCharacter(character, glyphData.glyph, 0);
234 // Assure that the font fallback glyph selection worked, aka. the fallbackGlyphData font data is not the same as before.
235 GlyphData fallbackGlyphData = font.glyphDataForCharacter(character, mirror);
236 ASSERT(fallbackGlyphData.fontData != fontData);
238 // Restore original state of the SVG Font glyph table and the current font fallback list,
239 // to assure the next lookup of the same glyph won't immediately return the fallback glyph.
240 page->setGlyphDataForCharacter(character, glyphData.glyph, fontData);
241 fontList->setGlyphPageZero(originalGlyphPageZero);
242 fontList->setGlyphPages(originalGlyphPages);
243 return fallbackGlyphData;