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