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/rendering/RenderObject.h" 28 #include "core/rendering/svg/RenderSVGInlineText.h" 29 #include "core/rendering/svg/RenderSVGResourceSolidColor.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 WebCore { 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 = static_cast<const SVGFontData*>(customFontData.get()); 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 (originalFontData && !originalFontData->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