1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
4 * Copyright (C) 2013 Google Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "config.h"
24 #include "core/css/resolver/FontBuilder.h"
25
26 #include "core/css/CSSCalculationValue.h"
27 #include "core/css/CSSToLengthConversionData.h"
28 #include "core/frame/LocalFrame.h"
29 #include "core/frame/Settings.h"
30 #include "core/rendering/RenderTheme.h"
31 #include "core/rendering/RenderView.h"
32 #include "core/rendering/TextAutosizer.h"
33 #include "platform/fonts/FontDescription.h"
34 #include "platform/text/LocaleToScriptMapping.h"
35
36 namespace blink {
37
38 // FIXME: This scoping class is a short-term fix to minimize the changes in
39 // Font-constructing logic.
40 class FontDescriptionChangeScope {
41 STACK_ALLOCATED();
42 public:
FontDescriptionChangeScope(FontBuilder * fontBuilder)43 FontDescriptionChangeScope(FontBuilder* fontBuilder)
44 : m_fontBuilder(fontBuilder)
45 , m_fontDescription(fontBuilder->m_style->fontDescription())
46 {
47 }
48
reset()49 void reset() { m_fontDescription = FontDescription(); }
set(const FontDescription & fontDescription)50 void set(const FontDescription& fontDescription) { m_fontDescription = fontDescription; }
fontDescription()51 FontDescription& fontDescription() { return m_fontDescription; }
52
~FontDescriptionChangeScope()53 ~FontDescriptionChangeScope()
54 {
55 m_fontBuilder->didChangeFontParameters(m_fontBuilder->m_style->setFontDescription(m_fontDescription));
56 }
57
58 private:
59 RawPtrWillBeMember<FontBuilder> m_fontBuilder;
60 FontDescription m_fontDescription;
61 };
62
FontBuilder()63 FontBuilder::FontBuilder()
64 : m_document(nullptr)
65 , m_style(0)
66 , m_fontDirty(false)
67 {
68 }
69
initForStyleResolve(const Document & document,RenderStyle * style)70 void FontBuilder::initForStyleResolve(const Document& document, RenderStyle* style)
71 {
72 ASSERT(document.frame());
73 m_document = &document;
74 m_style = style;
75 m_fontDirty = false;
76 }
77
setFontFamilyToStandard(FontDescription & fontDescription,const Document * document)78 inline static void setFontFamilyToStandard(FontDescription& fontDescription, const Document* document)
79 {
80 if (!document || !document->settings())
81 return;
82
83 fontDescription.setGenericFamily(FontDescription::StandardFamily);
84 const AtomicString& standardFontFamily = document->settings()->genericFontFamilySettings().standard();
85 if (standardFontFamily.isEmpty())
86 return;
87
88 fontDescription.firstFamily().setFamily(standardFontFamily);
89 // FIXME: Why is this needed here?
90 fontDescription.firstFamily().appendFamily(nullptr);
91 }
92
setInitial(float effectiveZoom)93 void FontBuilder::setInitial(float effectiveZoom)
94 {
95 ASSERT(m_document && m_document->settings());
96 if (!m_document || !m_document->settings())
97 return;
98
99 FontDescriptionChangeScope scope(this);
100
101 scope.reset();
102 setFontFamilyToStandard(scope.fontDescription(), m_document);
103 setSize(scope.fontDescription(), FontBuilder::initialSize());
104 }
105
inheritFrom(const FontDescription & fontDescription)106 void FontBuilder::inheritFrom(const FontDescription& fontDescription)
107 {
108 FontDescriptionChangeScope scope(this);
109
110 scope.set(fontDescription);
111 }
112
didChangeFontParameters(bool changed)113 void FontBuilder::didChangeFontParameters(bool changed)
114 {
115 m_fontDirty |= changed;
116 }
117
fromSystemFont(CSSValueID valueId,float effectiveZoom)118 void FontBuilder::fromSystemFont(CSSValueID valueId, float effectiveZoom)
119 {
120 FontDescriptionChangeScope scope(this);
121
122 FontDescription fontDescription;
123 RenderTheme::theme().systemFont(valueId, fontDescription);
124
125 // Double-check and see if the theme did anything. If not, don't bother updating the font.
126 if (!fontDescription.isAbsoluteSize())
127 return;
128
129 // Make sure the rendering mode and printer font settings are updated.
130 const Settings* settings = m_document->settings();
131 ASSERT(settings); // If we're doing style resolution, this document should always be in a frame and thus have settings
132 if (!settings)
133 return;
134
135 // Handle the zoom factor.
136 fontDescription.setComputedSize(getComputedSizeFromSpecifiedSize(fontDescription, effectiveZoom, fontDescription.specifiedSize()));
137 scope.set(fontDescription);
138 }
139
setFontFamilyInitial()140 void FontBuilder::setFontFamilyInitial()
141 {
142 FontDescriptionChangeScope scope(this);
143
144 setFontFamilyToStandard(scope.fontDescription(), m_document);
145 }
146
setFontFamilyInherit(const FontDescription & parentFontDescription)147 void FontBuilder::setFontFamilyInherit(const FontDescription& parentFontDescription)
148 {
149 FontDescriptionChangeScope scope(this);
150
151 scope.fontDescription().setGenericFamily(parentFontDescription.genericFamily());
152 scope.fontDescription().setFamily(parentFontDescription.family());
153 }
154
155 // FIXME: I am not convinced FontBuilder needs to know anything about CSSValues.
setFontFamilyValue(CSSValue * value)156 void FontBuilder::setFontFamilyValue(CSSValue* value)
157 {
158 FontDescriptionChangeScope scope(this);
159
160 if (!value->isValueList())
161 return;
162
163 FontFamily& firstFamily = scope.fontDescription().firstFamily();
164 FontFamily* currFamily = 0;
165
166 // Before mapping in a new font-family property, we should reset the generic family.
167 FixedPitchFontType oldFixedPitchFontType = scope.fontDescription().fixedPitchFontType();
168 scope.fontDescription().setGenericFamily(FontDescription::NoFamily);
169
170 for (CSSValueListIterator i = value; i.hasMore(); i.advance()) {
171 CSSValue* item = i.value();
172 if (!item->isPrimitiveValue())
173 continue;
174 CSSPrimitiveValue* contentValue = toCSSPrimitiveValue(item);
175 AtomicString face;
176 Settings* settings = m_document->settings();
177 if (contentValue->isString()) {
178 face = AtomicString(contentValue->getStringValue());
179 } else if (settings) {
180 switch (contentValue->getValueID()) {
181 case CSSValueWebkitBody:
182 face = settings->genericFontFamilySettings().standard();
183 break;
184 case CSSValueSerif:
185 face = FontFamilyNames::webkit_serif;
186 scope.fontDescription().setGenericFamily(FontDescription::SerifFamily);
187 break;
188 case CSSValueSansSerif:
189 face = FontFamilyNames::webkit_sans_serif;
190 scope.fontDescription().setGenericFamily(FontDescription::SansSerifFamily);
191 break;
192 case CSSValueCursive:
193 face = FontFamilyNames::webkit_cursive;
194 scope.fontDescription().setGenericFamily(FontDescription::CursiveFamily);
195 break;
196 case CSSValueFantasy:
197 face = FontFamilyNames::webkit_fantasy;
198 scope.fontDescription().setGenericFamily(FontDescription::FantasyFamily);
199 break;
200 case CSSValueMonospace:
201 face = FontFamilyNames::webkit_monospace;
202 scope.fontDescription().setGenericFamily(FontDescription::MonospaceFamily);
203 break;
204 case CSSValueWebkitPictograph:
205 face = FontFamilyNames::webkit_pictograph;
206 scope.fontDescription().setGenericFamily(FontDescription::PictographFamily);
207 break;
208 default:
209 break;
210 }
211 }
212
213 if (!face.isEmpty()) {
214 if (!currFamily) {
215 // Filling in the first family.
216 firstFamily.setFamily(face);
217 firstFamily.appendFamily(nullptr); // Remove any inherited family-fallback list.
218 currFamily = &firstFamily;
219 } else {
220 RefPtr<SharedFontFamily> newFamily = SharedFontFamily::create();
221 newFamily->setFamily(face);
222 currFamily->appendFamily(newFamily);
223 currFamily = newFamily.get();
224 }
225 }
226 }
227
228 // We can't call useFixedDefaultSize() until all new font families have been added
229 // If currFamily is non-zero then we set at least one family on this description.
230 if (!currFamily)
231 return;
232
233 if (scope.fontDescription().keywordSize() && scope.fontDescription().fixedPitchFontType() != oldFixedPitchFontType)
234 setSize(scope.fontDescription(), FontDescription::Size(scope.fontDescription().keywordSize(), 0.0f, false));
235 }
236
setWeight(FontWeight fontWeight)237 void FontBuilder::setWeight(FontWeight fontWeight)
238 {
239 FontDescriptionChangeScope scope(this);
240
241 scope.fontDescription().setWeight(fontWeight);
242 }
243
setSize(const FontDescription::Size & size)244 void FontBuilder::setSize(const FontDescription::Size& size)
245 {
246 FontDescriptionChangeScope scope(this);
247
248 setSize(scope.fontDescription(), size);
249 }
250
setStretch(FontStretch fontStretch)251 void FontBuilder::setStretch(FontStretch fontStretch)
252 {
253 FontDescriptionChangeScope scope(this);
254
255 scope.fontDescription().setStretch(fontStretch);
256 }
257
setScript(const String & locale)258 void FontBuilder::setScript(const String& locale)
259 {
260 FontDescriptionChangeScope scope(this);
261
262 scope.fontDescription().setLocale(locale);
263 scope.fontDescription().setScript(localeToScriptCodeForFontSelection(locale));
264 }
265
setStyle(FontStyle italic)266 void FontBuilder::setStyle(FontStyle italic)
267 {
268 FontDescriptionChangeScope scope(this);
269
270 scope.fontDescription().setStyle(italic);
271 }
272
setVariant(FontVariant smallCaps)273 void FontBuilder::setVariant(FontVariant smallCaps)
274 {
275 FontDescriptionChangeScope scope(this);
276
277 scope.fontDescription().setVariant(smallCaps);
278 }
279
setVariantLigatures(const FontDescription::VariantLigatures & ligatures)280 void FontBuilder::setVariantLigatures(const FontDescription::VariantLigatures& ligatures)
281 {
282 FontDescriptionChangeScope scope(this);
283
284 scope.fontDescription().setVariantLigatures(ligatures);
285 }
286
setTextRendering(TextRenderingMode textRenderingMode)287 void FontBuilder::setTextRendering(TextRenderingMode textRenderingMode)
288 {
289 FontDescriptionChangeScope scope(this);
290
291 scope.fontDescription().setTextRendering(textRenderingMode);
292 }
293
setKerning(FontDescription::Kerning kerning)294 void FontBuilder::setKerning(FontDescription::Kerning kerning)
295 {
296 FontDescriptionChangeScope scope(this);
297
298 scope.fontDescription().setKerning(kerning);
299 }
300
setFontSmoothing(FontSmoothingMode foontSmoothingMode)301 void FontBuilder::setFontSmoothing(FontSmoothingMode foontSmoothingMode)
302 {
303 FontDescriptionChangeScope scope(this);
304
305 scope.fontDescription().setFontSmoothing(foontSmoothingMode);
306 }
307
setFeatureSettings(PassRefPtr<FontFeatureSettings> settings)308 void FontBuilder::setFeatureSettings(PassRefPtr<FontFeatureSettings> settings)
309 {
310 FontDescriptionChangeScope scope(this);
311
312 scope.fontDescription().setFeatureSettings(settings);
313 }
314
setSize(FontDescription & fontDescription,const FontDescription::Size & size)315 void FontBuilder::setSize(FontDescription& fontDescription, const FontDescription::Size& size)
316 {
317 float specifiedSize = size.value;
318
319 if (!specifiedSize && size.keyword)
320 specifiedSize = FontSize::fontSizeForKeyword(m_document, size.keyword, fontDescription.fixedPitchFontType());
321
322 if (specifiedSize < 0)
323 return;
324
325 // Overly large font sizes will cause crashes on some platforms (such as Windows).
326 // Cap font size here to make sure that doesn't happen.
327 specifiedSize = std::min(maximumAllowedFontSize, specifiedSize);
328
329 fontDescription.setKeywordSize(size.keyword);
330 fontDescription.setSpecifiedSize(specifiedSize);
331 fontDescription.setIsAbsoluteSize(size.isAbsolute);
332 }
333
getComputedSizeFromSpecifiedSize(FontDescription & fontDescription,float effectiveZoom,float specifiedSize)334 float FontBuilder::getComputedSizeFromSpecifiedSize(FontDescription& fontDescription, float effectiveZoom, float specifiedSize)
335 {
336 float zoomFactor = effectiveZoom;
337 // FIXME: Why is this here!!!!?!
338 if (LocalFrame* frame = m_document->frame())
339 zoomFactor *= frame->textZoomFactor();
340
341 return FontSize::getComputedSizeFromSpecifiedSize(m_document, zoomFactor, fontDescription.isAbsoluteSize(), specifiedSize);
342 }
343
getFontAndGlyphOrientation(const RenderStyle * style,FontOrientation & fontOrientation,NonCJKGlyphOrientation & glyphOrientation)344 static void getFontAndGlyphOrientation(const RenderStyle* style, FontOrientation& fontOrientation, NonCJKGlyphOrientation& glyphOrientation)
345 {
346 if (style->isHorizontalWritingMode()) {
347 fontOrientation = Horizontal;
348 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
349 return;
350 }
351
352 switch (style->textOrientation()) {
353 case TextOrientationVerticalRight:
354 fontOrientation = Vertical;
355 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
356 return;
357 case TextOrientationUpright:
358 fontOrientation = Vertical;
359 glyphOrientation = NonCJKGlyphOrientationUpright;
360 return;
361 case TextOrientationSideways:
362 if (style->writingMode() == LeftToRightWritingMode) {
363 // FIXME: This should map to sideways-left, which is not supported yet.
364 fontOrientation = Vertical;
365 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
366 return;
367 }
368 fontOrientation = Horizontal;
369 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
370 return;
371 case TextOrientationSidewaysRight:
372 fontOrientation = Horizontal;
373 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
374 return;
375 default:
376 ASSERT_NOT_REACHED();
377 fontOrientation = Horizontal;
378 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
379 return;
380 }
381 }
382
checkForOrientationChange(RenderStyle * style)383 void FontBuilder::checkForOrientationChange(RenderStyle* style)
384 {
385 FontOrientation fontOrientation;
386 NonCJKGlyphOrientation glyphOrientation;
387 getFontAndGlyphOrientation(style, fontOrientation, glyphOrientation);
388
389 FontDescriptionChangeScope scope(this);
390
391 if (scope.fontDescription().orientation() == fontOrientation && scope.fontDescription().nonCJKGlyphOrientation() == glyphOrientation)
392 return;
393
394 scope.fontDescription().setNonCJKGlyphOrientation(glyphOrientation);
395 scope.fontDescription().setOrientation(fontOrientation);
396 }
397
checkForGenericFamilyChange(RenderStyle * style,const RenderStyle * parentStyle)398 void FontBuilder::checkForGenericFamilyChange(RenderStyle* style, const RenderStyle* parentStyle)
399 {
400 FontDescriptionChangeScope scope(this);
401
402 if (scope.fontDescription().isAbsoluteSize() || !parentStyle)
403 return;
404
405 const FontDescription& parentFontDescription = parentStyle->fontDescription();
406 if (scope.fontDescription().fixedPitchFontType() == parentFontDescription.fixedPitchFontType())
407 return;
408
409 // For now, lump all families but monospace together.
410 if (scope.fontDescription().genericFamily() != FontDescription::MonospaceFamily
411 && parentFontDescription.genericFamily() != FontDescription::MonospaceFamily)
412 return;
413
414 // We know the parent is monospace or the child is monospace, and that font
415 // size was unspecified. We want to scale our font size as appropriate.
416 // If the font uses a keyword size, then we refetch from the table rather than
417 // multiplying by our scale factor.
418 float size;
419 if (scope.fontDescription().keywordSize()) {
420 size = FontSize::fontSizeForKeyword(m_document, scope.fontDescription().keywordSize(), scope.fontDescription().fixedPitchFontType());
421 } else {
422 Settings* settings = m_document->settings();
423 float fixedScaleFactor = (settings && settings->defaultFixedFontSize() && settings->defaultFontSize())
424 ? static_cast<float>(settings->defaultFixedFontSize()) / settings->defaultFontSize()
425 : 1;
426 size = parentFontDescription.fixedPitchFontType() == FixedPitchFont ?
427 scope.fontDescription().specifiedSize() / fixedScaleFactor :
428 scope.fontDescription().specifiedSize() * fixedScaleFactor;
429 }
430
431 scope.fontDescription().setSpecifiedSize(size);
432 updateComputedSize(scope.fontDescription(), style);
433 }
434
updateComputedSize(RenderStyle * style,const RenderStyle * parentStyle)435 void FontBuilder::updateComputedSize(RenderStyle* style, const RenderStyle* parentStyle)
436 {
437 FontDescriptionChangeScope scope(this);
438 updateComputedSize(scope.fontDescription(), style);
439 }
440
updateComputedSize(FontDescription & fontDescription,RenderStyle * style)441 void FontBuilder::updateComputedSize(FontDescription& fontDescription, RenderStyle* style)
442 {
443 float computedSize = getComputedSizeFromSpecifiedSize(fontDescription, style->effectiveZoom(), fontDescription.specifiedSize());
444 float multiplier = style->textAutosizingMultiplier();
445 if (multiplier > 1)
446 computedSize = TextAutosizer::computeAutosizedFontSize(computedSize, multiplier);
447 fontDescription.setComputedSize(computedSize);
448 }
449
450 // FIXME: style param should come first
createFont(PassRefPtrWillBeRawPtr<FontSelector> fontSelector,const RenderStyle * parentStyle,RenderStyle * style)451 void FontBuilder::createFont(PassRefPtrWillBeRawPtr<FontSelector> fontSelector, const RenderStyle* parentStyle, RenderStyle* style)
452 {
453 if (!m_fontDirty)
454 return;
455
456 updateComputedSize(style, parentStyle);
457 checkForGenericFamilyChange(style, parentStyle);
458 checkForOrientationChange(style);
459 style->font().update(fontSelector);
460 m_fontDirty = false;
461 }
462
createFontForDocument(PassRefPtrWillBeRawPtr<FontSelector> fontSelector,RenderStyle * documentStyle)463 void FontBuilder::createFontForDocument(PassRefPtrWillBeRawPtr<FontSelector> fontSelector, RenderStyle* documentStyle)
464 {
465 FontDescription fontDescription = FontDescription();
466 fontDescription.setLocale(documentStyle->locale());
467 fontDescription.setScript(localeToScriptCodeForFontSelection(documentStyle->locale()));
468
469 setFontFamilyToStandard(fontDescription, m_document);
470
471 setSize(fontDescription, FontDescription::Size(FontSize::initialKeywordSize(), 0.0f, false));
472 updateComputedSize(fontDescription, documentStyle);
473
474 FontOrientation fontOrientation;
475 NonCJKGlyphOrientation glyphOrientation;
476 getFontAndGlyphOrientation(documentStyle, fontOrientation, glyphOrientation);
477 fontDescription.setOrientation(fontOrientation);
478 fontDescription.setNonCJKGlyphOrientation(glyphOrientation);
479 documentStyle->setFontDescription(fontDescription);
480 documentStyle->font().update(fontSelector);
481 }
482
483 }
484