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 "core/rendering/RenderObject.h"
27#include "core/rendering/svg/RenderSVGInlineText.h"
28#include "core/rendering/svg/RenderSVGResourceSolidColor.h"
29#include "core/svg/SVGFontData.h"
30#include "core/svg/SVGFontElement.h"
31#include "core/svg/SVGFontFaceElement.h"
32#include "core/svg/SVGGlyphElement.h"
33#include "platform/fonts/GlyphBuffer.h"
34#include "platform/fonts/WidthIterator.h"
35#include "platform/graphics/GraphicsContext.h"
36
37namespace WebCore {
38
39static inline const SVGFontData* svgFontAndFontFaceElementForFontData(const SimpleFontData* fontData, SVGFontFaceElement*& fontFace, SVGFontElement*& font)
40{
41    ASSERT(fontData);
42    ASSERT(fontData->isCustomFont());
43    ASSERT(fontData->isSVGFont());
44
45    RefPtr<CustomFontData> customFontData = fontData->customFontData();
46    const SVGFontData* svgFontData = static_cast<const SVGFontData*>(customFontData.get());
47
48    // FIXME crbug.com/359380 : The current editing impl references the font after the svg font nodes are removed.
49    if (svgFontData->shouldSkipDrawing())
50        return 0;
51
52    fontFace = svgFontData->svgFontFaceElement();
53    ASSERT(fontFace);
54
55    font = fontFace->associatedFontElement();
56    return svgFontData;
57}
58
59static inline RenderObject* firstParentRendererForNonTextNode(RenderObject* renderer)
60{
61    ASSERT(renderer);
62    return renderer->isText() ? renderer->parent() : renderer;
63}
64
65static inline RenderObject* renderObjectFromRun(const TextRun& run)
66{
67    if (TextRun::RenderingContext* renderingContext = run.renderingContext())
68        return static_cast<SVGTextRunRenderingContext*>(renderingContext)->renderer();
69    return 0;
70}
71
72static inline RenderSVGResource* activePaintingResourceFromRun(const TextRun& run)
73{
74    if (TextRun::RenderingContext* renderingContext = run.renderingContext())
75        return static_cast<SVGTextRunRenderingContext*>(renderingContext)->activePaintingResource();
76    return 0;
77}
78
79float SVGTextRunRenderingContext::floatWidthUsingSVGFont(const Font& font, const TextRun& run, int& charsConsumed, Glyph& glyphId) const
80{
81    WidthIterator it(&font, run);
82    GlyphBuffer glyphBuffer;
83    charsConsumed += it.advance(run.length(), &glyphBuffer);
84    glyphId = !glyphBuffer.isEmpty() ? glyphBuffer.glyphAt(0) : 0;
85    return it.runWidthSoFar();
86}
87
88void SVGTextRunRenderingContext::drawSVGGlyphs(GraphicsContext* context, const TextRun& run, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
89{
90    SVGFontElement* fontElement = 0;
91    SVGFontFaceElement* fontFaceElement = 0;
92
93    const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement);
94    if (!fontElement || !fontFaceElement)
95        return;
96
97    // We can only paint SVGFonts if a context is available.
98    RenderSVGResource* activePaintingResource = activePaintingResourceFromRun(run);
99    RenderObject* renderObject = renderObjectFromRun(run);
100    RenderObject* parentRenderObject = firstParentRendererForNonTextNode(renderObject);
101    RenderStyle* parentRenderObjectStyle = 0;
102
103    ASSERT(renderObject);
104    if (!activePaintingResource) {
105        // TODO: We're only supporting simple filled HTML text so far.
106        RenderSVGResourceSolidColor* solidPaintingResource = RenderSVGResource::sharedSolidPaintingResource();
107        solidPaintingResource->setColor(context->fillColor());
108        activePaintingResource = solidPaintingResource;
109    }
110
111    bool isVerticalText = false;
112    if (parentRenderObject) {
113        parentRenderObjectStyle = parentRenderObject->style();
114        ASSERT(parentRenderObjectStyle);
115        isVerticalText = parentRenderObjectStyle->svgStyle()->isVerticalWritingMode();
116    }
117
118    float scale = scaleEmToUnits(fontData->platformData().size(), fontFaceElement->unitsPerEm());
119    ASSERT(activePaintingResource);
120
121    FloatPoint glyphOrigin;
122    glyphOrigin.setX(svgFontData->horizontalOriginX() * scale);
123    glyphOrigin.setY(svgFontData->horizontalOriginY() * scale);
124
125    unsigned short resourceMode = context->textDrawingMode() == TextModeStroke ? ApplyToStrokeMode : ApplyToFillMode;
126    // From a resource perspective this ought to be treated as "text mode".
127    resourceMode |= ApplyToTextMode;
128
129    FloatPoint currentPoint = point;
130    for (int i = 0; i < numGlyphs; ++i) {
131        Glyph glyph = glyphBuffer.glyphAt(from + i);
132        if (!glyph)
133            continue;
134
135        float advance = glyphBuffer.advanceAt(from + i).width();
136        SVGGlyph svgGlyph = fontElement->svgGlyphForGlyph(glyph);
137        ASSERT(!svgGlyph.isPartOfLigature);
138        ASSERT(svgGlyph.tableEntry == glyph);
139
140        SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, svgFontData);
141
142        // FIXME: Support arbitary SVG content as glyph (currently limited to <glyph d="..."> situations).
143        if (svgGlyph.pathData.isEmpty()) {
144            if (isVerticalText)
145                currentPoint.move(0, advance);
146            else
147                currentPoint.move(advance, 0);
148            continue;
149         }
150
151        if (isVerticalText) {
152            glyphOrigin.setX(svgGlyph.verticalOriginX * scale);
153            glyphOrigin.setY(svgGlyph.verticalOriginY * scale);
154         }
155
156        AffineTransform glyphPathTransform;
157        glyphPathTransform.translate(currentPoint.x() + glyphOrigin.x(), currentPoint.y() + glyphOrigin.y());
158        glyphPathTransform.scale(scale, -scale);
159
160        Path glyphPath = svgGlyph.pathData;
161        glyphPath.transform(glyphPathTransform);
162
163        if (activePaintingResource->applyResource(parentRenderObject, parentRenderObjectStyle, context, resourceMode)) {
164            float strokeThickness = context->strokeThickness();
165            if (renderObject && renderObject->isSVGInlineText())
166                context->setStrokeThickness(strokeThickness * toRenderSVGInlineText(renderObject)->scalingFactor());
167            activePaintingResource->postApplyResource(parentRenderObject, context, resourceMode, &glyphPath, 0);
168            context->setStrokeThickness(strokeThickness);
169        }
170
171        if (isVerticalText)
172            currentPoint.move(0, advance);
173        else
174            currentPoint.move(advance, 0);
175    }
176}
177
178GlyphData SVGTextRunRenderingContext::glyphDataForCharacter(const Font& font, const TextRun& run, WidthIterator& iterator, UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength)
179{
180    const SimpleFontData* primaryFont = font.primaryFont();
181    ASSERT(primaryFont);
182
183    pair<GlyphData, GlyphPage*> pair = font.glyphDataAndPageForCharacter(character, mirror);
184    GlyphData glyphData = pair.first;
185
186    // Check if we have the missing glyph data, in which case we can just return.
187    GlyphData missingGlyphData = primaryFont->missingGlyphData();
188    if (glyphData.glyph == missingGlyphData.glyph && glyphData.fontData == missingGlyphData.fontData) {
189        ASSERT(glyphData.fontData);
190        return glyphData;
191    }
192
193    // Save data fromt he font fallback list because we may modify it later. Do this before the
194    // potential change to glyphData.fontData below.
195    FontFallbackList* fontList = font.fontList();
196    ASSERT(fontList);
197    FontFallbackList::GlyphPagesStateSaver glyphPagesSaver(*fontList);
198
199    // Characters enclosed by an <altGlyph> element, may not be registered in the GlyphPage.
200    const SimpleFontData* originalFontData = glyphData.fontData;
201    if (originalFontData && !originalFontData->isSVGFont()) {
202        if (TextRun::RenderingContext* renderingContext = run.renderingContext()) {
203            RenderObject* renderObject = static_cast<SVGTextRunRenderingContext*>(renderingContext)->renderer();
204            RenderObject* parentRenderObject = renderObject->isText() ? renderObject->parent() : renderObject;
205            ASSERT(parentRenderObject);
206            if (Element* parentRenderObjectElement = toElement(parentRenderObject->node())) {
207                if (isSVGAltGlyphElement(*parentRenderObjectElement))
208                    glyphData.fontData = primaryFont;
209            }
210        }
211    }
212
213    const SimpleFontData* fontData = glyphData.fontData;
214    if (fontData) {
215        if (!fontData->isSVGFont())
216            return glyphData;
217
218        SVGFontElement* fontElement = 0;
219        SVGFontFaceElement* fontFaceElement = 0;
220
221        const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement);
222        if (!fontElement || !fontFaceElement)
223            return glyphData;
224
225        // If we got here, we're dealing with a glyph defined in a SVG Font.
226        // The returned glyph by glyphDataAndPageForCharacter() is a glyph stored in the SVG Font glyph table.
227        // This doesn't necessarily mean the glyph is suitable for rendering/measuring in this context, its
228        // arabic-form/orientation/... may not match, we have to apply SVG Glyph selection to discover that.
229        if (svgFontData->applySVGGlyphSelection(iterator, glyphData, mirror, currentCharacter, advanceLength))
230            return glyphData;
231    }
232
233    GlyphPage* page = pair.second;
234    ASSERT(page);
235
236    // No suitable glyph found that is compatible with the requirments (same language, arabic-form, orientation etc.)
237    // Even though our GlyphPage contains an entry for eg. glyph "a", it's not compatible. So we have to temporarily
238    // remove the glyph data information from the GlyphPage, and retry the lookup, which handles font fallbacks correctly.
239    page->setGlyphDataForCharacter(character, 0, 0);
240
241    // Assure that the font fallback glyph selection worked, aka. the fallbackGlyphData font data is not the same as before.
242    GlyphData fallbackGlyphData = font.glyphDataForCharacter(character, mirror);
243    ASSERT(fallbackGlyphData.fontData != fontData);
244
245    // Restore original state of the SVG Font glyph table and the current font fallback list,
246    // to assure the next lookup of the same glyph won't immediately return the fallback glyph.
247    page->setGlyphDataForCharacter(character, glyphData.glyph, originalFontData);
248    ASSERT(fallbackGlyphData.fontData);
249    return fallbackGlyphData;
250}
251
252}
253
254#endif
255