blob: 325d5479b291a6d2eaa8c8aab16e8436d37b1675 [file] [log] [blame]
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001/*
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)53e740f2013-05-09 18:38:43 +010027#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 Murdoche69819b2013-07-17 14:56:49 +010031#include "core/svg/SVGFontFaceElement.h"
32#include "core/svg/SVGGlyphElement.h"
Torne (Richard Coles)09380292014-02-21 12:17:33 +000033#include "platform/fonts/Character.h"
Torne (Richard Coles)a854de02013-12-18 16:25:25 +000034#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)f5e4ad52013-08-05 13:57:57 +010038#include "wtf/text/StringBuilder.h"
39#include "wtf/unicode/CharacterNames.h"
40#include "wtf/unicode/Unicode.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010041
42using namespace WTF;
43using namespace Unicode;
44
45namespace WebCore {
46
47SVGFontData::SVGFontData(SVGFontFaceElement* fontFaceElement)
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +000048 : CustomFontData()
Torne (Richard Coles)51b29062013-11-28 11:56:03 +000049 , m_svgFontFaceElement(fontFaceElement)
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010050 , 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
60void 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)c0e19a62013-08-30 15:15:11 +010088 Glyph letterXGlyph = glyphPageZero->glyphForCharacter('x');
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010089 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)c0e19a62013-08-30 15:15:11 +0100109 Glyph spaceGlyph = glyphPageZero->glyphForCharacter(' ');
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100110 fontData->setSpaceGlyph(spaceGlyph);
111 fontData->setSpaceWidth(fontData->widthForGlyph(spaceGlyph));
112
113 // Estimate average character width.
Torne (Richard Coles)c0e19a62013-08-30 15:15:11 +0100114 Glyph numeralZeroGlyph = glyphPageZero->glyphForCharacter('0');
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100115 fontData->setAvgCharWidth(numeralZeroGlyph ? fontData->widthForGlyph(numeralZeroGlyph) : fontData->spaceWidth());
116
117 // Estimate maximum character width.
Torne (Richard Coles)c0e19a62013-08-30 15:15:11 +0100118 Glyph letterWGlyph = glyphPageZero->glyphForCharacter('W');
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100119 fontData->setMaxCharWidth(letterWGlyph ? fontData->widthForGlyph(letterWGlyph) : ascent);
120}
121
122float 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
135bool 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)09380292014-02-21 12:17:33 +0000146 remainingTextInRun = Character::normalizeSpaces(remainingTextInRun.characters8(), remainingTextInRun.length());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100147 } else {
148 remainingTextInRun = String(run.data16(currentCharacter), run.charactersLength() - currentCharacter);
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000149 remainingTextInRun = Character::normalizeSpaces(remainingTextInRun.characters16(), remainingTextInRun.length());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100150 }
151
152 if (mirror)
Ben Murdoche69819b2013-07-17 14:56:49 +0100153 remainingTextInRun = createStringWithMirroredCharacters(remainingTextInRun);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100154 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)09380292014-02-21 12:17:33 +0000169 Vector<AtomicString> altGlyphNames;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100170
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)d5428f32014-03-18 10:21:16 +0000179 if (isSVGAltGlyphElement(*parentRenderObjectElement)) {
180 if (!toSVGAltGlyphElement(*parentRenderObjectElement).hasValidGlyphElements(altGlyphNames))
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100181 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)d5428f32014-03-18 10:21:16 +0000190 associatedFontElement->collectGlyphsForAltGlyphReference(altGlyphNames[index], glyphs);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100191
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)53e740f2013-05-09 18:38:43 +0100201 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)53e740f2013-05-09 18:38:43 +0100215 glyphData.glyph = svgGlyph.tableEntry;
216 advanceLength = svgGlyph.unicodeStringLength;
217 return true;
218 }
219
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100220 return false;
221}
222
223bool 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
241bool 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
265bool 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 Murdoche69819b2013-07-17 14:56:49 +0100290String SVGFontData::createStringWithMirroredCharacters(const String& string) const
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100291{
Ben Murdoche69819b2013-07-17 14:56:49 +0100292 if (string.isEmpty())
293 return emptyString();
294
295 unsigned length = string.length();
296
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100297 StringBuilder mirroredCharacters;
298 mirroredCharacters.reserveCapacity(length);
299
Ben Murdoche69819b2013-07-17 14:56:49 +0100300 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)53e740f2013-05-09 18:38:43 +0100312 }
313
314 return mirroredCharacters.toString();
315}
316
317} // namespace WebCore
318
319#endif