Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2007, 2008 Nikolas Zimmermann <zimmermann (at) 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/platform/graphics/Font.h"
     28 #include "core/platform/graphics/GlyphBuffer.h"
     29 #include "core/platform/graphics/GraphicsContext.h"
     30 #include "core/platform/graphics/WidthIterator.h"
     31 #include "core/rendering/RenderObject.h"
     32 #include "core/rendering/svg/RenderSVGInlineText.h"
     33 #include "core/rendering/svg/RenderSVGResourceSolidColor.h"
     34 #include "core/svg/SVGFontData.h"
     35 #include "core/svg/SVGFontElement.h"
     36 #include "core/svg/SVGFontFaceElement.h"
     37 #include "core/svg/SVGGlyphElement.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, 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