r184718 and r184725 caused four tests to begin crashing
[WebKit-https.git] / Source / WebCore / svg / SVGFontData.cpp
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 "SVGFontData.h"
24
25 #include "GlyphPage.h"
26 #include "RenderElement.h"
27 #include "SVGAltGlyphElement.h"
28 #include "SVGFontElement.h"
29 #include "SVGFontFaceElement.h"
30 #include "SVGGlyph.h"
31 #include "SVGGlyphElement.h"
32 #include "SVGNames.h"
33 #include "SVGTextRunRenderingContext.h"
34 #include "TextRun.h"
35 #include "WidthIterator.h"
36 #include "XMLNames.h"
37 #include <wtf/text/StringBuilder.h>
38 #include <wtf/unicode/CharacterNames.h>
39
40 using namespace WTF;
41 using namespace Unicode;
42
43 namespace WebCore {
44
45 static String createStringWithMirroredCharacters(StringView);
46 static void computeNormalizedSpaces(const TextRun&, bool mirror, String& normalizedSpacesStringCache);
47
48 SVGFontData::SVGFontData(SVGFontFaceElement* fontFaceElement)
49     : m_svgFontFaceElement(fontFaceElement)
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::initializeFont(Font* font, float fontSize)
61 {
62     ASSERT(font);
63
64     SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement();
65     ASSERT(svgFontFaceElement);
66
67     font->setZeroWidthSpaceGlyph(0);
68     font->determinePitch();
69
70     unsigned unitsPerEm = svgFontFaceElement->unitsPerEm();
71     float scale = scaleEmToUnits(fontSize, unitsPerEm);
72     float xHeight = svgFontFaceElement->xHeight() * scale;
73     float ascent = svgFontFaceElement->ascent() * scale;
74     float descent = svgFontFaceElement->descent() * scale;
75     float lineGap = 0.1f * fontSize;
76
77     const GlyphPage* glyphPageZero = font->glyphPage(0);
78
79     if (!xHeight && glyphPageZero) {
80         // Fallback if x_heightAttr is not specified for the font element.
81         Glyph letterXGlyph = glyphPageZero->glyphDataForCharacter('x').glyph;
82         xHeight = letterXGlyph ? font->widthForGlyph(letterXGlyph) : 2 * ascent / 3;
83     }
84
85     FontMetrics& fontMetrics = font->fontMetrics();
86     fontMetrics.setUnitsPerEm(unitsPerEm);
87     fontMetrics.setAscent(ascent);
88     fontMetrics.setDescent(descent);
89     fontMetrics.setLineGap(lineGap);
90     fontMetrics.setLineSpacing(roundf(ascent) + roundf(descent) + roundf(lineGap));
91     fontMetrics.setXHeight(xHeight);
92
93     if (!glyphPageZero) {
94         font->setSpaceGlyph(0);
95         font->setSpaceWidths(0);
96         font->setAvgCharWidth(0);
97         font->setMaxCharWidth(ascent);
98         return;
99     }
100
101     // Calculate space width.
102     Glyph spaceGlyph = glyphPageZero->glyphDataForCharacter(' ').glyph;
103     font->setSpaceGlyph(spaceGlyph);
104     font->setSpaceWidths(font->widthForGlyph(spaceGlyph));
105
106     // Estimate average character width.
107     Glyph numeralZeroGlyph = glyphPageZero->glyphDataForCharacter('0').glyph;
108     font->setAvgCharWidth(numeralZeroGlyph ? font->widthForGlyph(numeralZeroGlyph) : font->spaceWidth());
109
110     // Estimate maximum character width.
111     Glyph letterWGlyph = glyphPageZero->glyphDataForCharacter('W').glyph;
112     font->setMaxCharWidth(letterWGlyph ? font->widthForGlyph(letterWGlyph) : ascent);
113 }
114
115 float SVGFontData::widthForSVGGlyph(Glyph glyph, float fontSize) const
116 {
117     SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement();
118     ASSERT(svgFontFaceElement);
119
120     SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement();
121     ASSERT(associatedFontElement);
122
123     SVGGlyph svgGlyph = associatedFontElement->svgGlyphForGlyph(glyph);
124     SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, this);
125     return svgGlyph.horizontalAdvanceX * scaleEmToUnits(fontSize, svgFontFaceElement->unitsPerEm());
126 }
127
128 bool SVGFontData::applySVGGlyphSelection(WidthIterator& iterator, GlyphData& glyphData, bool mirror, int currentCharacter, unsigned& advanceLength, String& normalizedSpacesStringCache) const
129 {
130     const TextRun& run = iterator.run();
131     Vector<SVGGlyph::ArabicForm>& arabicForms = iterator.arabicForms();
132     ASSERT(run.charactersLength() >= static_cast<unsigned>(currentCharacter));
133
134     SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement();
135     ASSERT(svgFontFaceElement);
136
137     SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement();
138     ASSERT(associatedFontElement);
139
140     RenderObject* renderObject = nullptr;
141     if (TextRun::RenderingContext* renderingContext = run.renderingContext())
142         renderObject = &static_cast<SVGTextRunRenderingContext*>(renderingContext)->renderer();
143
144     String language;
145     bool isVerticalText = false;
146     Vector<String> altGlyphNames;
147
148     if (renderObject) {
149         RenderElement& parentRenderer = is<RenderElement>(*renderObject) ? downcast<RenderElement>(*renderObject) : *renderObject->parent();
150
151         isVerticalText = parentRenderer.style().svgStyle().isVerticalWritingMode();
152         if (Element* parentRendererElement = parentRenderer.element()) {
153             language = parentRendererElement->getAttribute(XMLNames::langAttr);
154
155             if (is<SVGAltGlyphElement>(*parentRendererElement)) {
156                 SVGAltGlyphElement& altGlyph = downcast<SVGAltGlyphElement>(*parentRendererElement);
157                 if (!altGlyph.hasValidGlyphElements(altGlyphNames))
158                     altGlyphNames.clear();
159             }
160         }
161     }
162
163     Vector<SVGGlyph> glyphs;
164     size_t altGlyphNamesSize = altGlyphNames.size();
165     if (altGlyphNamesSize) {
166         for (size_t index = 0; index < altGlyphNamesSize; ++index)
167             associatedFontElement->collectGlyphsForGlyphName(altGlyphNames[index], glyphs);
168
169         // Assign the unicodeStringLength now that its known.
170         size_t glyphsSize = glyphs.size();
171         for (size_t i = 0; i < glyphsSize; ++i)
172             glyphs[i].unicodeStringLength = run.length();
173
174         // Do not check alt glyphs for compatibility. Just return the first one.
175         // Later code will fail if we do not do this and the glyph is incompatible.
176         if (glyphsSize) {
177             SVGGlyph& svgGlyph = glyphs[0];
178             iterator.setLastGlyphName(svgGlyph.glyphName);
179             glyphData.glyph = svgGlyph.tableEntry;
180             advanceLength = svgGlyph.unicodeStringLength;
181             return true;
182         }
183     } else {
184         // Associate text with arabic forms, if needed.
185         computeNormalizedSpaces(run, mirror, normalizedSpacesStringCache);
186         auto remainingTextInRun = normalizedSpacesStringCache.substring(currentCharacter);
187
188         if (!currentCharacter && arabicForms.isEmpty())
189             arabicForms = charactersWithArabicForm(remainingTextInRun, mirror);
190
191         associatedFontElement->collectGlyphsForString(remainingTextInRun, glyphs);
192     }
193
194     size_t glyphsSize = glyphs.size();
195     for (size_t i = 0; i < glyphsSize; ++i) {
196         SVGGlyph& svgGlyph = glyphs[i];
197         if (svgGlyph.isPartOfLigature)
198             continue;
199         if (!isCompatibleGlyph(svgGlyph, isVerticalText, language, arabicForms, currentCharacter, currentCharacter + svgGlyph.unicodeStringLength))
200             continue;
201         iterator.setLastGlyphName(svgGlyph.glyphName);
202         glyphData.glyph = svgGlyph.tableEntry;
203         advanceLength = svgGlyph.unicodeStringLength;
204         return true;
205     }
206
207     iterator.setLastGlyphName(String());
208     return false;
209 }
210
211 bool SVGFontData::fillSVGGlyphPage(GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const Font* font) const
212 {
213     ASSERT(font->isCustomFont());
214     ASSERT(font->isSVGFont());
215
216     SVGFontFaceElement* fontFaceElement = this->svgFontFaceElement();
217     ASSERT(fontFaceElement);
218
219     SVGFontElement* fontElement = fontFaceElement->associatedFontElement();
220     ASSERT(fontElement);
221
222     if (bufferLength == length)
223         return fillBMPGlyphs(fontElement, pageToFill, offset, length, buffer, font);
224
225     ASSERT(bufferLength == 2 * length);
226     return fillNonBMPGlyphs(fontElement, pageToFill, offset, length, buffer, font);
227 }
228
229 bool SVGFontData::fillBMPGlyphs(SVGFontElement* fontElement, GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, const Font* font) const
230 {
231     bool haveGlyphs = false;
232     Vector<SVGGlyph> glyphs;
233     for (unsigned i = 0; i < length; ++i) {
234         String lookupString(buffer + i, 1);
235         fontElement->collectGlyphsForString(lookupString, glyphs);
236         if (glyphs.isEmpty()) {
237             pageToFill->setGlyphDataForIndex(offset + i, 0, 0);
238             continue;
239         }
240
241         // Associate entry in glyph page with first valid SVGGlyph.
242         // If there are multiple valid ones, just take the first one. WidthIterator will take
243         // care of matching to the correct glyph, if multiple ones are available, as that's
244         // only possible within the context of a string (eg. arabic form matching).
245         haveGlyphs = true;
246         pageToFill->setGlyphDataForIndex(offset + i, glyphs.first().tableEntry, font);
247         glyphs.clear();
248     }
249
250     return haveGlyphs;
251 }
252
253 bool SVGFontData::fillNonBMPGlyphs(SVGFontElement* fontElement, GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, const Font* font) const
254 {
255     bool haveGlyphs = false;
256     Vector<SVGGlyph> glyphs;
257     for (unsigned i = 0; i < length; ++i) {
258         // Each character here consists of a surrogate pair
259         String lookupString(buffer + i * 2, 2);
260         fontElement->collectGlyphsForString(lookupString, glyphs);
261         if (glyphs.isEmpty()) {
262             pageToFill->setGlyphDataForIndex(offset + i, 0, 0);
263             continue;
264         }
265
266         // Associate entry in glyph page with first valid SVGGlyph.
267         // If there are multiple valid ones, just take the first one. WidthIterator will take
268         // care of matching to the correct glyph, if multiple ones are available, as that's
269         // only possible within the context of a string (eg. arabic form matching).
270         haveGlyphs = true;
271         pageToFill->setGlyphDataForIndex(offset + i, glyphs.first().tableEntry, font);
272         glyphs.clear();
273     }
274
275     return haveGlyphs;
276 }
277
278 void computeNormalizedSpaces(const TextRun& run, bool mirror, String& normalizedSpacesStringCache)
279 {
280     if (normalizedSpacesStringCache.length() == static_cast<unsigned>(run.charactersLength()))
281         return;
282     if (run.is8Bit())
283         normalizedSpacesStringCache = FontCascade::normalizeSpaces(run.characters8(), run.charactersLength());
284     else
285         normalizedSpacesStringCache = FontCascade::normalizeSpaces(run.characters16(), run.charactersLength());
286     if (mirror)
287         normalizedSpacesStringCache = createStringWithMirroredCharacters(normalizedSpacesStringCache);
288 }
289
290 String createStringWithMirroredCharacters(StringView string)
291 {
292     unsigned length = string.length();
293     StringBuilder mirroredCharacters;
294     mirroredCharacters.reserveCapacity(length);
295     for (unsigned i = 0; i < length; ) {
296         UChar32 character;
297         U16_NEXT(string, i, length, character);
298         mirroredCharacters.append(u_charMirror(character));
299     }
300     return mirroredCharacters.toString();
301 }
302
303 } // namespace WebCore
304
305 #endif