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