OLD | NEW |
| (Empty) |
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 "core/SVGNames.h" | |
26 #include "core/XMLNames.h" | |
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" | |
31 #include "core/svg/SVGFontFaceElement.h" | |
32 #include "core/svg/SVGGlyphElement.h" | |
33 #include "platform/fonts/Character.h" | |
34 #include "platform/fonts/SVGGlyph.h" | |
35 #include "platform/fonts/SimpleFontData.h" | |
36 #include "platform/fonts/shaping/SimpleShaper.h" | |
37 #include "platform/text/TextRun.h" | |
38 #include "wtf/text/StringBuilder.h" | |
39 #include "wtf/unicode/CharacterNames.h" | |
40 #include "wtf/unicode/Unicode.h" | |
41 | |
42 using namespace WTF; | |
43 using namespace Unicode; | |
44 | |
45 namespace blink { | |
46 | |
47 SVGFontData::SVGFontData(SVGFontFaceElement* fontFaceElement) | |
48 : CustomFontData() | |
49 , m_svgFontFaceElement(fontFaceElement->createWeakRef()) | |
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 SVGFontData::~SVGFontData() | |
61 { | |
62 } | |
63 | |
64 void SVGFontData::initializeFontData(SimpleFontData* fontData, float fontSize) | |
65 { | |
66 ASSERT(fontData); | |
67 | |
68 SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement(); | |
69 | |
70 SVGFontElement* svgFontElement = svgFontFaceElement->associatedFontElement()
; | |
71 ASSERT(svgFontElement); | |
72 GlyphData missingGlyphData; | |
73 missingGlyphData.fontData = fontData; | |
74 missingGlyphData.glyph = svgFontElement->missingGlyph(); | |
75 fontData->setMissingGlyphData(missingGlyphData); | |
76 | |
77 fontData->setZeroWidthSpaceGlyph(0); | |
78 fontData->determinePitch(); | |
79 | |
80 unsigned unitsPerEm = svgFontFaceElement->unitsPerEm(); | |
81 float scale = scaleEmToUnits(fontSize, unitsPerEm); | |
82 float xHeight = svgFontFaceElement->xHeight() * scale; | |
83 float ascent = svgFontFaceElement->ascent() * scale; | |
84 float descent = svgFontFaceElement->descent() * scale; | |
85 float lineGap = 0.1f * fontSize; | |
86 | |
87 GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(fontData, 0)->pag
e(); | |
88 | |
89 if (!xHeight && glyphPageZero) { | |
90 // Fallback if x_heightAttr is not specified for the font element. | |
91 Glyph letterXGlyph = glyphPageZero->glyphForCharacter('x'); | |
92 xHeight = letterXGlyph ? fontData->widthForGlyph(letterXGlyph) : 2 * asc
ent / 3; | |
93 } | |
94 | |
95 FontMetrics& fontMetrics = fontData->fontMetrics(); | |
96 fontMetrics.setUnitsPerEm(unitsPerEm); | |
97 fontMetrics.setAscent(ascent); | |
98 fontMetrics.setDescent(descent); | |
99 fontMetrics.setLineGap(lineGap); | |
100 fontMetrics.setLineSpacing(roundf(ascent) + roundf(descent) + roundf(lineGap
)); | |
101 fontMetrics.setXHeight(xHeight); | |
102 | |
103 if (!glyphPageZero) { | |
104 fontData->setSpaceGlyph(0); | |
105 fontData->setSpaceWidth(0); | |
106 fontData->setAvgCharWidth(0); | |
107 fontData->setMaxCharWidth(ascent); | |
108 return; | |
109 } | |
110 | |
111 // Calculate space width. | |
112 Glyph spaceGlyph = glyphPageZero->glyphForCharacter(' '); | |
113 fontData->setSpaceGlyph(spaceGlyph); | |
114 fontData->setSpaceWidth(fontData->widthForGlyph(spaceGlyph)); | |
115 | |
116 // Estimate average character width. | |
117 Glyph numeralZeroGlyph = glyphPageZero->glyphForCharacter('0'); | |
118 fontData->setAvgCharWidth(numeralZeroGlyph ? fontData->widthForGlyph(numeral
ZeroGlyph) : fontData->spaceWidth()); | |
119 | |
120 // Estimate maximum character width. | |
121 Glyph letterWGlyph = glyphPageZero->glyphForCharacter('W'); | |
122 fontData->setMaxCharWidth(letterWGlyph ? fontData->widthForGlyph(letterWGlyp
h) : ascent); | |
123 } | |
124 | |
125 float SVGFontData::widthForSVGGlyph(Glyph glyph, float fontSize) const | |
126 { | |
127 // FIXME: (http://crbug.com/359380) Width calculation may be triggered after
removeNode from the current editing impl. | |
128 // The retrieved width is not being used, so here we return a dummy value. | |
129 if (shouldSkipDrawing()) | |
130 return 0.0; | |
131 | |
132 SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement(); | |
133 | |
134 // RenderView::clearSelection is invoked while removing some element, e.g. | |
135 // Document::nodeWillBeRemoved => FrameSelection::nodeWillBeRemoved => Rende
rView::clearSelection. | |
136 // Since recalc style has not been executed yet, RenderStyle might have some
reference to | |
137 // SVGFontFaceElement which was also removed. | |
138 // In this case, use default horizontalAdvanceX instead of associatedFontEle
ment's one. | |
139 if (!svgFontFaceElement->inDocument()) | |
140 return m_horizontalAdvanceX * scaleEmToUnits(fontSize, svgFontFaceElemen
t->unitsPerEm()); | |
141 | |
142 SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontEl
ement(); | |
143 ASSERT(associatedFontElement); | |
144 | |
145 SVGGlyph svgGlyph = associatedFontElement->svgGlyphForGlyph(glyph); | |
146 SVGGlyphElement::inheritUnspecifiedAttributes(svgGlyph, this); | |
147 return svgGlyph.horizontalAdvanceX * scaleEmToUnits(fontSize, svgFontFaceEle
ment->unitsPerEm()); | |
148 } | |
149 | |
150 bool SVGFontData::applySVGGlyphSelection(SimpleShaper& shaper, GlyphData& glyphD
ata, bool mirror, int currentCharacter, unsigned& advanceLength) const | |
151 { | |
152 const TextRun& run = shaper.run(); | |
153 Vector<SVGGlyph::ArabicForm>& arabicForms = shaper.arabicForms(); | |
154 ASSERT(int(run.charactersLength()) >= currentCharacter); | |
155 | |
156 // Associate text with arabic forms, if needed. | |
157 String remainingTextInRun; | |
158 | |
159 if (run.is8Bit()) { | |
160 remainingTextInRun = String(run.data8(currentCharacter), run.charactersL
ength() - currentCharacter); | |
161 remainingTextInRun = Character::normalizeSpaces(remainingTextInRun.chara
cters8(), remainingTextInRun.length()); | |
162 } else { | |
163 remainingTextInRun = String(run.data16(currentCharacter), run.characters
Length() - currentCharacter); | |
164 remainingTextInRun = Character::normalizeSpaces(remainingTextInRun.chara
cters16(), remainingTextInRun.length()); | |
165 } | |
166 | |
167 if (mirror) | |
168 remainingTextInRun = createStringWithMirroredCharacters(remainingTextInR
un); | |
169 if (!currentCharacter && arabicForms.isEmpty()) | |
170 arabicForms = charactersWithArabicForm(remainingTextInRun, mirror); | |
171 | |
172 SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement(); | |
173 SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontEl
ement(); | |
174 ASSERT(associatedFontElement); | |
175 | |
176 RenderObject* renderObject = 0; | |
177 if (TextRun::RenderingContext* renderingContext = run.renderingContext()) | |
178 renderObject = static_cast<SVGTextRunRenderingContext*>(renderingContext
)->renderer(); | |
179 | |
180 String language; | |
181 bool isVerticalText = false; | |
182 Vector<AtomicString> altGlyphNames; | |
183 | |
184 if (renderObject) { | |
185 RenderObject* parentRenderObject = renderObject->isText() ? renderObject
->parent() : renderObject; | |
186 ASSERT(parentRenderObject); | |
187 | |
188 isVerticalText = parentRenderObject->style()->svgStyle().isVerticalWriti
ngMode(); | |
189 if (Element* parentRenderObjectElement = toElement(parentRenderObject->n
ode())) { | |
190 language = parentRenderObjectElement->getAttribute(XMLNames::langAtt
r); | |
191 | |
192 if (isSVGAltGlyphElement(*parentRenderObjectElement)) { | |
193 if (!toSVGAltGlyphElement(*parentRenderObjectElement).hasValidGl
yphElements(altGlyphNames)) | |
194 altGlyphNames.clear(); | |
195 } | |
196 } | |
197 } | |
198 | |
199 Vector<SVGGlyph> glyphs; | |
200 size_t altGlyphNamesSize = altGlyphNames.size(); | |
201 if (altGlyphNamesSize) { | |
202 for (size_t index = 0; index < altGlyphNamesSize; ++index) | |
203 associatedFontElement->collectGlyphsForAltGlyphReference(altGlyphNam
es[index], glyphs); | |
204 | |
205 // Assign the unicodeStringLength now that its known. | |
206 size_t glyphsSize = glyphs.size(); | |
207 for (size_t i = 0; i < glyphsSize; ++i) | |
208 glyphs[i].unicodeStringLength = run.length(); | |
209 | |
210 // Do not check alt glyphs for compatibility. Just return the first one. | |
211 // Later code will fail if we do not do this and the glyph is incompatib
le. | |
212 if (glyphsSize) { | |
213 SVGGlyph& svgGlyph = glyphs[0]; | |
214 glyphData.glyph = svgGlyph.tableEntry; | |
215 advanceLength = svgGlyph.unicodeStringLength; | |
216 return true; | |
217 } | |
218 } else | |
219 associatedFontElement->collectGlyphsForString(remainingTextInRun, glyphs
); | |
220 | |
221 size_t glyphsSize = glyphs.size(); | |
222 for (size_t i = 0; i < glyphsSize; ++i) { | |
223 SVGGlyph& svgGlyph = glyphs[i]; | |
224 if (svgGlyph.isPartOfLigature) | |
225 continue; | |
226 if (!isCompatibleGlyph(svgGlyph, isVerticalText, language, arabicForms,
currentCharacter, currentCharacter + svgGlyph.unicodeStringLength)) | |
227 continue; | |
228 glyphData.glyph = svgGlyph.tableEntry; | |
229 advanceLength = svgGlyph.unicodeStringLength; | |
230 return true; | |
231 } | |
232 | |
233 return false; | |
234 } | |
235 | |
236 bool SVGFontData::fillSVGGlyphPage(GlyphPage* pageToFill, unsigned offset, unsig
ned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData
) const | |
237 { | |
238 ASSERT(fontData->isCustomFont()); | |
239 ASSERT(fontData->isSVGFont()); | |
240 | |
241 SVGFontFaceElement* fontFaceElement = this->svgFontFaceElement(); | |
242 SVGFontElement* fontElement = fontFaceElement->associatedFontElement(); | |
243 ASSERT(fontElement); | |
244 | |
245 if (bufferLength == length) | |
246 return fillBMPGlyphs(fontElement, pageToFill, offset, length, buffer, fo
ntData); | |
247 | |
248 ASSERT(bufferLength == 2 * length); | |
249 return fillNonBMPGlyphs(fontElement, pageToFill, offset, length, buffer, fon
tData); | |
250 } | |
251 | |
252 bool SVGFontData::fillBMPGlyphs(SVGFontElement* fontElement, GlyphPage* pageToFi
ll, unsigned offset, unsigned length, UChar* buffer, const SimpleFontData* fontD
ata) const | |
253 { | |
254 bool haveGlyphs = false; | |
255 Vector<SVGGlyph> glyphs; | |
256 for (unsigned i = 0; i < length; ++i) { | |
257 String lookupString(buffer + i, 1); | |
258 fontElement->collectGlyphsForString(lookupString, glyphs); | |
259 if (glyphs.isEmpty()) | |
260 continue; | |
261 | |
262 // Associate entry in glyph page with first valid SVGGlyph. | |
263 // If there are multiple valid ones, just take the first one. SimpleShap
er will take | |
264 // care of matching to the correct glyph, if multiple ones are available
, as that's | |
265 // only possible within the context of a string (eg. arabic form matchin
g). | |
266 haveGlyphs = true; | |
267 pageToFill->setGlyphDataForIndex(offset + i, glyphs.first().tableEntry,
fontData); | |
268 glyphs.clear(); | |
269 } | |
270 | |
271 return haveGlyphs; | |
272 } | |
273 | |
274 bool SVGFontData::fillNonBMPGlyphs(SVGFontElement* fontElement, GlyphPage* pageT
oFill, unsigned offset, unsigned length, UChar* buffer, const SimpleFontData* fo
ntData) const | |
275 { | |
276 bool haveGlyphs = false; | |
277 Vector<SVGGlyph> glyphs; | |
278 for (unsigned i = 0; i < length; ++i) { | |
279 // Each character here consists of a surrogate pair | |
280 String lookupString(buffer + i * 2, 2); | |
281 fontElement->collectGlyphsForString(lookupString, glyphs); | |
282 if (glyphs.isEmpty()) | |
283 continue; | |
284 | |
285 // Associate entry in glyph page with first valid SVGGlyph. | |
286 // If there are multiple valid ones, just take the first one. SimpleShap
er will take | |
287 // care of matching to the correct glyph, if multiple ones are available
, as that's | |
288 // only possible within the context of a string (eg. arabic form matchin
g). | |
289 haveGlyphs = true; | |
290 pageToFill->setGlyphDataForIndex(offset + i, glyphs.first().tableEntry,
fontData); | |
291 glyphs.clear(); | |
292 } | |
293 | |
294 return haveGlyphs; | |
295 } | |
296 | |
297 String SVGFontData::createStringWithMirroredCharacters(const String& string) con
st | |
298 { | |
299 if (string.isEmpty()) | |
300 return emptyString(); | |
301 | |
302 unsigned length = string.length(); | |
303 | |
304 StringBuilder mirroredCharacters; | |
305 mirroredCharacters.reserveCapacity(length); | |
306 | |
307 if (string.is8Bit()) { | |
308 const LChar* characters = string.characters8(); | |
309 for (unsigned i = 0; i < length; ++i) | |
310 mirroredCharacters.append(mirroredChar(characters[i])); | |
311 } else { | |
312 const UChar* characters = string.characters16(); | |
313 unsigned i = 0; | |
314 while (i < length) { | |
315 UChar32 character; | |
316 U16_NEXT(characters, i, length, character); | |
317 mirroredCharacters.append(mirroredChar(character)); | |
318 } | |
319 } | |
320 | |
321 return mirroredCharacters.toString(); | |
322 } | |
323 | |
324 SVGFontFaceElement* SVGFontData::svgFontFaceElement() const | |
325 { | |
326 // FIXME: SVGFontData should be only used from the document with the SVGFont
FaceElement. | |
327 RELEASE_ASSERT(m_svgFontFaceElement && m_svgFontFaceElement->inDocument()); | |
328 return m_svgFontFaceElement.get(); | |
329 } | |
330 | |
331 bool SVGFontData::shouldSkipDrawing() const | |
332 { | |
333 // FIXME: (http://crbug.com/359380) Glyph may be referenced after removeNode
from the current editing impl. | |
334 return !m_svgFontFaceElement || !m_svgFontFaceElement->inDocument(); | |
335 } | |
336 | |
337 } // namespace blink | |
338 | |
339 #endif | |
OLD | NEW |