Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2008 Nikolas Zimmermann <zimmermann@kde.org> |
| 3 | * |
| 4 | * This library is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU Library General Public |
| 6 | * License as published by the Free Software Foundation; either |
| 7 | * version 2 of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * This library is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | * Library General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU Library General Public License |
| 15 | * along with this library; see the file COPYING.LIB. If not, write to |
| 16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 17 | * Boston, MA 02110-1301, USA. |
| 18 | */ |
| 19 | |
| 20 | #include "config.h" |
| 21 | |
| 22 | #if ENABLE(SVG_FONTS) |
| 23 | #include "core/svg/SVGFontData.h" |
| 24 | |
| 25 | #include "SVGNames.h" |
| 26 | #include "XMLNames.h" |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 27 | #include "core/rendering/RenderObject.h" |
| 28 | #include "core/rendering/svg/SVGTextRunRenderingContext.h" |
| 29 | #include "core/svg/SVGAltGlyphElement.h" |
| 30 | #include "core/svg/SVGFontElement.h" |
Ben Murdoch | e69819b | 2013-07-17 14:56:49 +0100 | [diff] [blame] | 31 | #include "core/svg/SVGFontFaceElement.h" |
| 32 | #include "core/svg/SVGGlyphElement.h" |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame] | 33 | #include "platform/fonts/Character.h" |
Torne (Richard Coles) | a854de0 | 2013-12-18 16:25:25 +0000 | [diff] [blame] | 34 | #include "platform/fonts/SVGGlyph.h" |
| 35 | #include "platform/fonts/SimpleFontData.h" |
| 36 | #include "platform/fonts/WidthIterator.h" |
| 37 | #include "platform/text/TextRun.h" |
Torne (Richard Coles) | f5e4ad5 | 2013-08-05 13:57:57 +0100 | [diff] [blame] | 38 | #include "wtf/text/StringBuilder.h" |
| 39 | #include "wtf/unicode/CharacterNames.h" |
| 40 | #include "wtf/unicode/Unicode.h" |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 41 | |
| 42 | using namespace WTF; |
| 43 | using namespace Unicode; |
| 44 | |
| 45 | namespace WebCore { |
| 46 | |
| 47 | SVGFontData::SVGFontData(SVGFontFaceElement* fontFaceElement) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame^] | 48 | : CustomFontData() |
Torne (Richard Coles) | 51b2906 | 2013-11-28 11:56:03 +0000 | [diff] [blame] | 49 | , m_svgFontFaceElement(fontFaceElement) |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 50 | , m_horizontalOriginX(fontFaceElement->horizontalOriginX()) |
| 51 | , m_horizontalOriginY(fontFaceElement->horizontalOriginY()) |
| 52 | , m_horizontalAdvanceX(fontFaceElement->horizontalAdvanceX()) |
| 53 | , m_verticalOriginX(fontFaceElement->verticalOriginX()) |
| 54 | , m_verticalOriginY(fontFaceElement->verticalOriginY()) |
| 55 | , m_verticalAdvanceY(fontFaceElement->verticalAdvanceY()) |
| 56 | { |
| 57 | ASSERT_ARG(fontFaceElement, fontFaceElement); |
| 58 | } |
| 59 | |
| 60 | void SVGFontData::initializeFontData(SimpleFontData* fontData, float fontSize) |
| 61 | { |
| 62 | ASSERT(fontData); |
| 63 | |
| 64 | SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement(); |
| 65 | ASSERT(svgFontFaceElement); |
| 66 | |
| 67 | SVGFontElement* svgFontElement = svgFontFaceElement->associatedFontElement(); |
| 68 | ASSERT(svgFontElement); |
| 69 | GlyphData missingGlyphData; |
| 70 | missingGlyphData.fontData = fontData; |
| 71 | missingGlyphData.glyph = svgFontElement->missingGlyph(); |
| 72 | fontData->setMissingGlyphData(missingGlyphData); |
| 73 | |
| 74 | fontData->setZeroWidthSpaceGlyph(0); |
| 75 | fontData->determinePitch(); |
| 76 | |
| 77 | unsigned unitsPerEm = svgFontFaceElement->unitsPerEm(); |
| 78 | float scale = scaleEmToUnits(fontSize, unitsPerEm); |
| 79 | float xHeight = svgFontFaceElement->xHeight() * scale; |
| 80 | float ascent = svgFontFaceElement->ascent() * scale; |
| 81 | float descent = svgFontFaceElement->descent() * scale; |
| 82 | float lineGap = 0.1f * fontSize; |
| 83 | |
| 84 | GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(fontData, 0)->page(); |
| 85 | |
| 86 | if (!xHeight && glyphPageZero) { |
| 87 | // Fallback if x_heightAttr is not specified for the font element. |
Torne (Richard Coles) | c0e19a6 | 2013-08-30 15:15:11 +0100 | [diff] [blame] | 88 | Glyph letterXGlyph = glyphPageZero->glyphForCharacter('x'); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 89 | xHeight = letterXGlyph ? fontData->widthForGlyph(letterXGlyph) : 2 * ascent / 3; |
| 90 | } |
| 91 | |
| 92 | FontMetrics& fontMetrics = fontData->fontMetrics(); |
| 93 | fontMetrics.setUnitsPerEm(unitsPerEm); |
| 94 | fontMetrics.setAscent(ascent); |
| 95 | fontMetrics.setDescent(descent); |
| 96 | fontMetrics.setLineGap(lineGap); |
| 97 | fontMetrics.setLineSpacing(roundf(ascent) + roundf(descent) + roundf(lineGap)); |
| 98 | fontMetrics.setXHeight(xHeight); |
| 99 | |
| 100 | if (!glyphPageZero) { |
| 101 | fontData->setSpaceGlyph(0); |
| 102 | fontData->setSpaceWidth(0); |
| 103 | fontData->setAvgCharWidth(0); |
| 104 | fontData->setMaxCharWidth(ascent); |
| 105 | return; |
| 106 | } |
| 107 | |
| 108 | // Calculate space width. |
Torne (Richard Coles) | c0e19a6 | 2013-08-30 15:15:11 +0100 | [diff] [blame] | 109 | Glyph spaceGlyph = glyphPageZero->glyphForCharacter(' '); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 110 | fontData->setSpaceGlyph(spaceGlyph); |
| 111 | fontData->setSpaceWidth(fontData->widthForGlyph(spaceGlyph)); |
| 112 | |
| 113 | // Estimate average character width. |
Torne (Richard Coles) | c0e19a6 | 2013-08-30 15:15:11 +0100 | [diff] [blame] | 114 | Glyph numeralZeroGlyph = glyphPageZero->glyphForCharacter('0'); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 115 | fontData->setAvgCharWidth(numeralZeroGlyph ? fontData->widthForGlyph(numeralZeroGlyph) : fontData->spaceWidth()); |
| 116 | |
| 117 | // Estimate maximum character width. |
Torne (Richard Coles) | c0e19a6 | 2013-08-30 15:15:11 +0100 | [diff] [blame] | 118 | Glyph letterWGlyph = glyphPageZero->glyphForCharacter('W'); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 119 | fontData->setMaxCharWidth(letterWGlyph ? fontData->widthForGlyph(letterWGlyph) : ascent); |
| 120 | } |
| 121 | |
| 122 | float SVGFontData::widthForSVGGlyph(Glyph glyph, float fontSize) const |
| 123 | { |
| 124 | SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement(); |
| 125 | ASSERT(svgFontFaceElement); |
| 126 | |
| 127 | SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement(); |
| 128 | ASSERT(associatedFontElement); |
| 129 | |
| 130 | SVGGlyph svgGlyph = associatedFontElement->svgGlyphForGlyph(glyph); |
| 131 | SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, this); |
| 132 | return svgGlyph.horizontalAdvanceX * scaleEmToUnits(fontSize, svgFontFaceElement->unitsPerEm()); |
| 133 | } |
| 134 | |
| 135 | bool SVGFontData::applySVGGlyphSelection(WidthIterator& iterator, GlyphData& glyphData, bool mirror, int currentCharacter, unsigned& advanceLength) const |
| 136 | { |
| 137 | const TextRun& run = iterator.run(); |
| 138 | Vector<SVGGlyph::ArabicForm>& arabicForms = iterator.arabicForms(); |
| 139 | ASSERT(int(run.charactersLength()) >= currentCharacter); |
| 140 | |
| 141 | // Associate text with arabic forms, if needed. |
| 142 | String remainingTextInRun; |
| 143 | |
| 144 | if (run.is8Bit()) { |
| 145 | remainingTextInRun = String(run.data8(currentCharacter), run.charactersLength() - currentCharacter); |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame] | 146 | remainingTextInRun = Character::normalizeSpaces(remainingTextInRun.characters8(), remainingTextInRun.length()); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 147 | } else { |
| 148 | remainingTextInRun = String(run.data16(currentCharacter), run.charactersLength() - currentCharacter); |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame] | 149 | remainingTextInRun = Character::normalizeSpaces(remainingTextInRun.characters16(), remainingTextInRun.length()); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 150 | } |
| 151 | |
| 152 | if (mirror) |
Ben Murdoch | e69819b | 2013-07-17 14:56:49 +0100 | [diff] [blame] | 153 | remainingTextInRun = createStringWithMirroredCharacters(remainingTextInRun); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 154 | if (!currentCharacter && arabicForms.isEmpty()) |
| 155 | arabicForms = charactersWithArabicForm(remainingTextInRun, mirror); |
| 156 | |
| 157 | SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement(); |
| 158 | ASSERT(svgFontFaceElement); |
| 159 | |
| 160 | SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement(); |
| 161 | ASSERT(associatedFontElement); |
| 162 | |
| 163 | RenderObject* renderObject = 0; |
| 164 | if (TextRun::RenderingContext* renderingContext = run.renderingContext()) |
| 165 | renderObject = static_cast<SVGTextRunRenderingContext*>(renderingContext)->renderer(); |
| 166 | |
| 167 | String language; |
| 168 | bool isVerticalText = false; |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame] | 169 | Vector<AtomicString> altGlyphNames; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 170 | |
| 171 | if (renderObject) { |
| 172 | RenderObject* parentRenderObject = renderObject->isText() ? renderObject->parent() : renderObject; |
| 173 | ASSERT(parentRenderObject); |
| 174 | |
| 175 | isVerticalText = parentRenderObject->style()->svgStyle()->isVerticalWritingMode(); |
| 176 | if (Element* parentRenderObjectElement = toElement(parentRenderObject->node())) { |
| 177 | language = parentRenderObjectElement->getAttribute(XMLNames::langAttr); |
| 178 | |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame^] | 179 | if (isSVGAltGlyphElement(*parentRenderObjectElement)) { |
| 180 | if (!toSVGAltGlyphElement(*parentRenderObjectElement).hasValidGlyphElements(altGlyphNames)) |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 181 | altGlyphNames.clear(); |
| 182 | } |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | Vector<SVGGlyph> glyphs; |
| 187 | size_t altGlyphNamesSize = altGlyphNames.size(); |
| 188 | if (altGlyphNamesSize) { |
| 189 | for (size_t index = 0; index < altGlyphNamesSize; ++index) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame^] | 190 | associatedFontElement->collectGlyphsForAltGlyphReference(altGlyphNames[index], glyphs); |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 191 | |
| 192 | // Assign the unicodeStringLength now that its known. |
| 193 | size_t glyphsSize = glyphs.size(); |
| 194 | for (size_t i = 0; i < glyphsSize; ++i) |
| 195 | glyphs[i].unicodeStringLength = run.length(); |
| 196 | |
| 197 | // Do not check alt glyphs for compatibility. Just return the first one. |
| 198 | // Later code will fail if we do not do this and the glyph is incompatible. |
| 199 | if (glyphsSize) { |
| 200 | SVGGlyph& svgGlyph = glyphs[0]; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 201 | glyphData.glyph = svgGlyph.tableEntry; |
| 202 | advanceLength = svgGlyph.unicodeStringLength; |
| 203 | return true; |
| 204 | } |
| 205 | } else |
| 206 | associatedFontElement->collectGlyphsForString(remainingTextInRun, glyphs); |
| 207 | |
| 208 | size_t glyphsSize = glyphs.size(); |
| 209 | for (size_t i = 0; i < glyphsSize; ++i) { |
| 210 | SVGGlyph& svgGlyph = glyphs[i]; |
| 211 | if (svgGlyph.isPartOfLigature) |
| 212 | continue; |
| 213 | if (!isCompatibleGlyph(svgGlyph, isVerticalText, language, arabicForms, currentCharacter, currentCharacter + svgGlyph.unicodeStringLength)) |
| 214 | continue; |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 215 | glyphData.glyph = svgGlyph.tableEntry; |
| 216 | advanceLength = svgGlyph.unicodeStringLength; |
| 217 | return true; |
| 218 | } |
| 219 | |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 220 | return false; |
| 221 | } |
| 222 | |
| 223 | bool SVGFontData::fillSVGGlyphPage(GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) const |
| 224 | { |
| 225 | ASSERT(fontData->isCustomFont()); |
| 226 | ASSERT(fontData->isSVGFont()); |
| 227 | |
| 228 | SVGFontFaceElement* fontFaceElement = this->svgFontFaceElement(); |
| 229 | ASSERT(fontFaceElement); |
| 230 | |
| 231 | SVGFontElement* fontElement = fontFaceElement->associatedFontElement(); |
| 232 | ASSERT(fontElement); |
| 233 | |
| 234 | if (bufferLength == length) |
| 235 | return fillBMPGlyphs(fontElement, pageToFill, offset, length, buffer, fontData); |
| 236 | |
| 237 | ASSERT(bufferLength == 2 * length); |
| 238 | return fillNonBMPGlyphs(fontElement, pageToFill, offset, length, buffer, fontData); |
| 239 | } |
| 240 | |
| 241 | bool SVGFontData::fillBMPGlyphs(SVGFontElement* fontElement, GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, const SimpleFontData* fontData) const |
| 242 | { |
| 243 | bool haveGlyphs = false; |
| 244 | Vector<SVGGlyph> glyphs; |
| 245 | for (unsigned i = 0; i < length; ++i) { |
| 246 | String lookupString(buffer + i, 1); |
| 247 | fontElement->collectGlyphsForString(lookupString, glyphs); |
| 248 | if (glyphs.isEmpty()) { |
| 249 | pageToFill->setGlyphDataForIndex(offset + i, 0, 0); |
| 250 | continue; |
| 251 | } |
| 252 | |
| 253 | // Associate entry in glyph page with first valid SVGGlyph. |
| 254 | // If there are multiple valid ones, just take the first one. WidthIterator will take |
| 255 | // care of matching to the correct glyph, if multiple ones are available, as that's |
| 256 | // only possible within the context of a string (eg. arabic form matching). |
| 257 | haveGlyphs = true; |
| 258 | pageToFill->setGlyphDataForIndex(offset + i, glyphs.first().tableEntry, fontData); |
| 259 | glyphs.clear(); |
| 260 | } |
| 261 | |
| 262 | return haveGlyphs; |
| 263 | } |
| 264 | |
| 265 | bool SVGFontData::fillNonBMPGlyphs(SVGFontElement* fontElement, GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, const SimpleFontData* fontData) const |
| 266 | { |
| 267 | bool haveGlyphs = false; |
| 268 | Vector<SVGGlyph> glyphs; |
| 269 | for (unsigned i = 0; i < length; ++i) { |
| 270 | // Each character here consists of a surrogate pair |
| 271 | String lookupString(buffer + i * 2, 2); |
| 272 | fontElement->collectGlyphsForString(lookupString, glyphs); |
| 273 | if (glyphs.isEmpty()) { |
| 274 | pageToFill->setGlyphDataForIndex(offset + i, 0, 0); |
| 275 | continue; |
| 276 | } |
| 277 | |
| 278 | // Associate entry in glyph page with first valid SVGGlyph. |
| 279 | // If there are multiple valid ones, just take the first one. WidthIterator will take |
| 280 | // care of matching to the correct glyph, if multiple ones are available, as that's |
| 281 | // only possible within the context of a string (eg. arabic form matching). |
| 282 | haveGlyphs = true; |
| 283 | pageToFill->setGlyphDataForIndex(offset + i, glyphs.first().tableEntry, fontData); |
| 284 | glyphs.clear(); |
| 285 | } |
| 286 | |
| 287 | return haveGlyphs; |
| 288 | } |
| 289 | |
Ben Murdoch | e69819b | 2013-07-17 14:56:49 +0100 | [diff] [blame] | 290 | String SVGFontData::createStringWithMirroredCharacters(const String& string) const |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 291 | { |
Ben Murdoch | e69819b | 2013-07-17 14:56:49 +0100 | [diff] [blame] | 292 | if (string.isEmpty()) |
| 293 | return emptyString(); |
| 294 | |
| 295 | unsigned length = string.length(); |
| 296 | |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 297 | StringBuilder mirroredCharacters; |
| 298 | mirroredCharacters.reserveCapacity(length); |
| 299 | |
Ben Murdoch | e69819b | 2013-07-17 14:56:49 +0100 | [diff] [blame] | 300 | if (string.is8Bit()) { |
| 301 | const LChar* characters = string.characters8(); |
| 302 | for (unsigned i = 0; i < length; ++i) |
| 303 | mirroredCharacters.append(mirroredChar(characters[i])); |
| 304 | } else { |
| 305 | const UChar* characters = string.characters16(); |
| 306 | unsigned i = 0; |
| 307 | while (i < length) { |
| 308 | UChar32 character; |
| 309 | U16_NEXT(characters, i, length, character); |
| 310 | mirroredCharacters.append(mirroredChar(character)); |
| 311 | } |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 312 | } |
| 313 | |
| 314 | return mirroredCharacters.toString(); |
| 315 | } |
| 316 | |
| 317 | } // namespace WebCore |
| 318 | |
| 319 | #endif |