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.
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.
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.
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.
24 #include "core/css/resolver/FontBuilder.h"
26 #include "core/css/CSSCalculationValue.h"
27 #include "core/css/CSSToLengthConversionData.h"
28 #include "core/css/FontSize.h"
29 #include "core/frame/LocalFrame.h"
30 #include "core/frame/Settings.h"
31 #include "core/rendering/RenderTheme.h"
32 #include "core/rendering/RenderView.h"
33 #include "core/rendering/TextAutosizer.h"
34 #include "platform/fonts/FontDescription.h"
35 #include "platform/text/LocaleToScriptMapping.h"
39 // FIXME: This scoping class is a short-term fix to minimize the changes in
40 // Font-constructing logic.
41 class FontDescriptionChangeScope {
44 FontDescriptionChangeScope(FontBuilder* fontBuilder)
45 : m_fontBuilder(fontBuilder)
46 , m_fontDescription(fontBuilder->m_style->fontDescription())
50 void reset() { m_fontDescription = FontDescription(); }
51 void set(const FontDescription& fontDescription) { m_fontDescription = fontDescription; }
52 FontDescription& fontDescription() { return m_fontDescription; }
54 ~FontDescriptionChangeScope()
56 m_fontBuilder->didChangeFontParameters(m_fontBuilder->m_style->setFontDescription(m_fontDescription));
60 RawPtrWillBeMember<FontBuilder> m_fontBuilder;
61 FontDescription m_fontDescription;
64 FontBuilder::FontBuilder()
66 , m_fontSizehasViewportUnits(false)
72 void FontBuilder::initForStyleResolve(const Document& document, RenderStyle* style)
74 ASSERT(document.frame());
75 m_document = &document;
80 inline static void setFontFamilyToStandard(FontDescription& fontDescription, const Document* document)
82 if (!document || !document->settings())
85 fontDescription.setGenericFamily(FontDescription::StandardFamily);
86 const AtomicString& standardFontFamily = document->settings()->genericFontFamilySettings().standard();
87 if (standardFontFamily.isEmpty())
90 fontDescription.firstFamily().setFamily(standardFontFamily);
91 // FIXME: Why is this needed here?
92 fontDescription.firstFamily().appendFamily(nullptr);
95 void FontBuilder::setInitial(float effectiveZoom)
97 ASSERT(m_document && m_document->settings());
98 if (!m_document || !m_document->settings())
101 FontDescriptionChangeScope scope(this);
104 setFontFamilyToStandard(scope.fontDescription(), m_document);
105 scope.fontDescription().setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1);
106 setSize(scope.fontDescription(), effectiveZoom, FontSize::fontSizeForKeyword(m_document, CSSValueMedium, NonFixedPitchFont));
109 void FontBuilder::inheritFrom(const FontDescription& fontDescription)
111 FontDescriptionChangeScope scope(this);
113 scope.set(fontDescription);
116 void FontBuilder::didChangeFontParameters(bool changed)
118 m_fontDirty |= changed;
121 void FontBuilder::fromSystemFont(CSSValueID valueId, float effectiveZoom)
123 FontDescriptionChangeScope scope(this);
125 FontDescription fontDescription;
126 RenderTheme::theme().systemFont(valueId, fontDescription);
128 // Double-check and see if the theme did anything. If not, don't bother updating the font.
129 if (!fontDescription.isAbsoluteSize())
132 // Make sure the rendering mode and printer font settings are updated.
133 const Settings* settings = m_document->settings();
134 ASSERT(settings); // If we're doing style resolution, this document should always be in a frame and thus have settings
138 // Handle the zoom factor.
139 fontDescription.setComputedSize(getComputedSizeFromSpecifiedSize(fontDescription, effectiveZoom, fontDescription.specifiedSize()));
140 scope.set(fontDescription);
143 void FontBuilder::setFontFamilyInitial()
145 FontDescriptionChangeScope scope(this);
147 setFontFamilyToStandard(scope.fontDescription(), m_document);
150 void FontBuilder::setFontFamilyInherit(const FontDescription& parentFontDescription)
152 FontDescriptionChangeScope scope(this);
154 scope.fontDescription().setGenericFamily(parentFontDescription.genericFamily());
155 scope.fontDescription().setFamily(parentFontDescription.family());
158 // FIXME: I am not convinced FontBuilder needs to know anything about CSSValues.
159 void FontBuilder::setFontFamilyValue(CSSValue* value)
161 FontDescriptionChangeScope scope(this);
163 if (!value->isValueList())
166 FontFamily& firstFamily = scope.fontDescription().firstFamily();
167 FontFamily* currFamily = 0;
169 // Before mapping in a new font-family property, we should reset the generic family.
170 FixedPitchFontType oldFixedPitchFontType = scope.fontDescription().fixedPitchFontType();
171 scope.fontDescription().setGenericFamily(FontDescription::NoFamily);
173 for (CSSValueListIterator i = value; i.hasMore(); i.advance()) {
174 CSSValue* item = i.value();
175 if (!item->isPrimitiveValue())
177 CSSPrimitiveValue* contentValue = toCSSPrimitiveValue(item);
179 Settings* settings = m_document->settings();
180 if (contentValue->isString()) {
181 face = AtomicString(contentValue->getStringValue());
182 } else if (settings) {
183 switch (contentValue->getValueID()) {
184 case CSSValueWebkitBody:
185 face = settings->genericFontFamilySettings().standard();
188 face = FontFamilyNames::webkit_serif;
189 scope.fontDescription().setGenericFamily(FontDescription::SerifFamily);
191 case CSSValueSansSerif:
192 face = FontFamilyNames::webkit_sans_serif;
193 scope.fontDescription().setGenericFamily(FontDescription::SansSerifFamily);
195 case CSSValueCursive:
196 face = FontFamilyNames::webkit_cursive;
197 scope.fontDescription().setGenericFamily(FontDescription::CursiveFamily);
199 case CSSValueFantasy:
200 face = FontFamilyNames::webkit_fantasy;
201 scope.fontDescription().setGenericFamily(FontDescription::FantasyFamily);
203 case CSSValueMonospace:
204 face = FontFamilyNames::webkit_monospace;
205 scope.fontDescription().setGenericFamily(FontDescription::MonospaceFamily);
207 case CSSValueWebkitPictograph:
208 face = FontFamilyNames::webkit_pictograph;
209 scope.fontDescription().setGenericFamily(FontDescription::PictographFamily);
216 if (!face.isEmpty()) {
218 // Filling in the first family.
219 firstFamily.setFamily(face);
220 firstFamily.appendFamily(nullptr); // Remove any inherited family-fallback list.
221 currFamily = &firstFamily;
223 RefPtr<SharedFontFamily> newFamily = SharedFontFamily::create();
224 newFamily->setFamily(face);
225 currFamily->appendFamily(newFamily);
226 currFamily = newFamily.get();
231 // We can't call useFixedDefaultSize() until all new font families have been added
232 // If currFamily is non-zero then we set at least one family on this description.
236 if (scope.fontDescription().keywordSize() && scope.fontDescription().fixedPitchFontType() != oldFixedPitchFontType) {
237 scope.fontDescription().setSpecifiedSize(FontSize::fontSizeForKeyword(m_document,
238 static_cast<CSSValueID>(CSSValueXxSmall + scope.fontDescription().keywordSize() - 1), scope.fontDescription().fixedPitchFontType()));
242 void FontBuilder::setFontSizeInitial()
244 FontDescriptionChangeScope scope(this);
246 float size = FontSize::fontSizeForKeyword(m_document, CSSValueMedium, scope.fontDescription().fixedPitchFontType());
251 scope.fontDescription().setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1);
252 scope.fontDescription().setSpecifiedSize(size);
255 void FontBuilder::setFontSizeInherit(const FontDescription& parentFontDescription)
257 FontDescriptionChangeScope scope(this);
259 float size = parentFontDescription.specifiedSize();
264 scope.fontDescription().setKeywordSize(parentFontDescription.keywordSize());
265 scope.fontDescription().setSpecifiedSize(size);
268 // FIXME: Figure out where we fall in the size ranges (xx-small to xxx-large)
269 // and scale down/up to the next size level.
270 static float largerFontSize(float size)
275 static float smallerFontSize(float size)
280 // FIXME: Have to pass RenderStyles here for calc/computed values. This shouldn't be neecessary.
281 void FontBuilder::setFontSizeValue(CSSValue* value, RenderStyle* parentStyle, const RenderStyle* rootElementStyle)
283 if (!value->isPrimitiveValue())
286 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
288 FontDescriptionChangeScope scope(this);
290 scope.fontDescription().setKeywordSize(0);
291 float parentSize = 0;
292 bool parentIsAbsoluteSize = false;
295 // FIXME: Find out when parentStyle could be 0?
297 parentSize = parentStyle->fontDescription().specifiedSize();
298 parentIsAbsoluteSize = parentStyle->fontDescription().isAbsoluteSize();
301 if (CSSValueID valueID = primitiveValue->getValueID()) {
303 case CSSValueXxSmall:
309 case CSSValueXxLarge:
310 case CSSValueWebkitXxxLarge:
311 size = FontSize::fontSizeForKeyword(m_document, valueID, scope.fontDescription().fixedPitchFontType());
312 scope.fontDescription().setKeywordSize(valueID - CSSValueXxSmall + 1);
315 size = largerFontSize(parentSize);
317 case CSSValueSmaller:
318 size = smallerFontSize(parentSize);
324 scope.fontDescription().setIsAbsoluteSize(parentIsAbsoluteSize && (valueID == CSSValueLarger || valueID == CSSValueSmaller));
326 scope.fontDescription().setIsAbsoluteSize(parentIsAbsoluteSize || !(primitiveValue->isPercentage() || primitiveValue->isFontRelativeLength()));
327 if (primitiveValue->isPercentage()) {
328 size = (primitiveValue->getFloatValue() * parentSize) / 100.0f;
330 // If we have viewport units the conversion will mark the parent style as having viewport units.
331 bool parentHasViewportUnits = parentStyle->hasViewportUnits();
332 parentStyle->setHasViewportUnits(false);
333 CSSToLengthConversionData conversionData(parentStyle, rootElementStyle, m_document->renderView(), 1.0f, true);
334 if (primitiveValue->isLength())
335 size = primitiveValue->computeLength<float>(conversionData);
336 else if (primitiveValue->isCalculatedPercentageWithLength())
337 size = primitiveValue->cssCalcValue()->toCalcValue(conversionData)->evaluate(parentSize);
339 ASSERT_NOT_REACHED();
340 m_fontSizehasViewportUnits = parentStyle->hasViewportUnits();
341 parentStyle->setHasViewportUnits(parentHasViewportUnits);
348 // Overly large font sizes will cause crashes on some platforms (such as Windows).
349 // Cap font size here to make sure that doesn't happen.
350 size = std::min(maximumAllowedFontSize, size);
353 scope.fontDescription().setSpecifiedSize(size);
356 void FontBuilder::setWeight(FontWeight fontWeight)
358 FontDescriptionChangeScope scope(this);
360 scope.fontDescription().setWeight(fontWeight);
363 void FontBuilder::setStretch(FontStretch fontStretch)
365 FontDescriptionChangeScope scope(this);
367 scope.fontDescription().setStretch(fontStretch);
370 void FontBuilder::setScript(const String& locale)
372 FontDescriptionChangeScope scope(this);
374 scope.fontDescription().setLocale(locale);
375 scope.fontDescription().setScript(localeToScriptCodeForFontSelection(locale));
378 void FontBuilder::setStyle(FontStyle italic)
380 FontDescriptionChangeScope scope(this);
382 scope.fontDescription().setStyle(italic);
385 void FontBuilder::setVariant(FontVariant smallCaps)
387 FontDescriptionChangeScope scope(this);
389 scope.fontDescription().setVariant(smallCaps);
392 void FontBuilder::setVariantLigatures(const FontDescription::VariantLigatures& ligatures)
394 FontDescriptionChangeScope scope(this);
396 scope.fontDescription().setVariantLigatures(ligatures);
399 void FontBuilder::setTextRendering(TextRenderingMode textRenderingMode)
401 FontDescriptionChangeScope scope(this);
403 scope.fontDescription().setTextRendering(textRenderingMode);
406 void FontBuilder::setKerning(FontDescription::Kerning kerning)
408 FontDescriptionChangeScope scope(this);
410 scope.fontDescription().setKerning(kerning);
413 void FontBuilder::setFontSmoothing(FontSmoothingMode foontSmoothingMode)
415 FontDescriptionChangeScope scope(this);
417 scope.fontDescription().setFontSmoothing(foontSmoothingMode);
420 void FontBuilder::setFeatureSettings(PassRefPtr<FontFeatureSettings> settings)
422 FontDescriptionChangeScope scope(this);
424 scope.fontDescription().setFeatureSettings(settings);
427 void FontBuilder::setSize(FontDescription& fontDescription, float effectiveZoom, float size)
429 fontDescription.setSpecifiedSize(size);
430 fontDescription.setComputedSize(getComputedSizeFromSpecifiedSize(fontDescription, effectiveZoom, size));
433 float FontBuilder::getComputedSizeFromSpecifiedSize(FontDescription& fontDescription, float effectiveZoom, float specifiedSize)
435 float zoomFactor = effectiveZoom;
436 // FIXME: Why is this here!!!!?!
437 if (LocalFrame* frame = m_document->frame())
438 zoomFactor *= frame->textZoomFactor();
440 return FontSize::getComputedSizeFromSpecifiedSize(m_document, zoomFactor, fontDescription.isAbsoluteSize(), specifiedSize);
443 static void getFontAndGlyphOrientation(const RenderStyle* style, FontOrientation& fontOrientation, NonCJKGlyphOrientation& glyphOrientation)
445 if (style->isHorizontalWritingMode()) {
446 fontOrientation = Horizontal;
447 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
451 switch (style->textOrientation()) {
452 case TextOrientationVerticalRight:
453 fontOrientation = Vertical;
454 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
456 case TextOrientationUpright:
457 fontOrientation = Vertical;
458 glyphOrientation = NonCJKGlyphOrientationUpright;
460 case TextOrientationSideways:
461 if (style->writingMode() == LeftToRightWritingMode) {
462 // FIXME: This should map to sideways-left, which is not supported yet.
463 fontOrientation = Vertical;
464 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
467 fontOrientation = Horizontal;
468 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
470 case TextOrientationSidewaysRight:
471 fontOrientation = Horizontal;
472 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
475 ASSERT_NOT_REACHED();
476 fontOrientation = Horizontal;
477 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
482 void FontBuilder::checkForOrientationChange(RenderStyle* style)
484 FontOrientation fontOrientation;
485 NonCJKGlyphOrientation glyphOrientation;
486 getFontAndGlyphOrientation(style, fontOrientation, glyphOrientation);
488 FontDescriptionChangeScope scope(this);
490 if (scope.fontDescription().orientation() == fontOrientation && scope.fontDescription().nonCJKGlyphOrientation() == glyphOrientation)
493 scope.fontDescription().setNonCJKGlyphOrientation(glyphOrientation);
494 scope.fontDescription().setOrientation(fontOrientation);
497 void FontBuilder::checkForGenericFamilyChange(RenderStyle* style, const RenderStyle* parentStyle)
499 FontDescriptionChangeScope scope(this);
501 if (scope.fontDescription().isAbsoluteSize() || !parentStyle)
504 const FontDescription& parentFontDescription = parentStyle->fontDescription();
505 if (scope.fontDescription().fixedPitchFontType() == parentFontDescription.fixedPitchFontType())
508 // For now, lump all families but monospace together.
509 if (scope.fontDescription().genericFamily() != FontDescription::MonospaceFamily
510 && parentFontDescription.genericFamily() != FontDescription::MonospaceFamily)
513 // We know the parent is monospace or the child is monospace, and that font
514 // size was unspecified. We want to scale our font size as appropriate.
515 // If the font uses a keyword size, then we refetch from the table rather than
516 // multiplying by our scale factor.
518 if (scope.fontDescription().keywordSize()) {
519 size = FontSize::fontSizeForKeyword(m_document, static_cast<CSSValueID>(CSSValueXxSmall + scope.fontDescription().keywordSize() - 1), scope.fontDescription().fixedPitchFontType());
521 Settings* settings = m_document->settings();
522 float fixedScaleFactor = (settings && settings->defaultFixedFontSize() && settings->defaultFontSize())
523 ? static_cast<float>(settings->defaultFixedFontSize()) / settings->defaultFontSize()
525 size = parentFontDescription.fixedPitchFontType() == FixedPitchFont ?
526 scope.fontDescription().specifiedSize() / fixedScaleFactor :
527 scope.fontDescription().specifiedSize() * fixedScaleFactor;
530 setSize(scope.fontDescription(), style->effectiveZoom(), size);
533 void FontBuilder::updateComputedSize(RenderStyle* style, const RenderStyle* parentStyle)
535 FontDescriptionChangeScope scope(this);
537 float computedSize = getComputedSizeFromSpecifiedSize(scope.fontDescription(), style->effectiveZoom(), scope.fontDescription().specifiedSize());
538 float multiplier = style->textAutosizingMultiplier();
540 computedSize = TextAutosizer::computeAutosizedFontSize(computedSize, multiplier);
542 scope.fontDescription().setComputedSize(computedSize);
545 // FIXME: style param should come first
546 void FontBuilder::createFont(PassRefPtrWillBeRawPtr<FontSelector> fontSelector, const RenderStyle* parentStyle, RenderStyle* style)
551 updateComputedSize(style, parentStyle);
552 checkForGenericFamilyChange(style, parentStyle);
553 checkForOrientationChange(style);
554 style->font().update(fontSelector);
558 void FontBuilder::createFontForDocument(PassRefPtrWillBeRawPtr<FontSelector> fontSelector, RenderStyle* documentStyle)
560 FontDescription fontDescription = FontDescription();
561 fontDescription.setScript(localeToScriptCodeForFontSelection(documentStyle->locale()));
563 setFontFamilyToStandard(fontDescription, m_document);
564 fontDescription.setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1);
565 int size = FontSize::fontSizeForKeyword(m_document, CSSValueMedium, NonFixedPitchFont);
566 fontDescription.setSpecifiedSize(size);
567 fontDescription.setComputedSize(getComputedSizeFromSpecifiedSize(fontDescription, documentStyle->effectiveZoom(), size));
569 FontOrientation fontOrientation;
570 NonCJKGlyphOrientation glyphOrientation;
571 getFontAndGlyphOrientation(documentStyle, fontOrientation, glyphOrientation);
572 fontDescription.setOrientation(fontOrientation);
573 fontDescription.setNonCJKGlyphOrientation(glyphOrientation);
574 documentStyle->setFontDescription(fontDescription);
575 documentStyle->font().update(fontSelector);