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/CSSFontFeatureValue.h"
28 #include "core/css/CSSToLengthConversionData.h"
29 #include "core/css/FontSize.h"
30 #include "core/frame/LocalFrame.h"
31 #include "core/frame/Settings.h"
32 #include "core/rendering/RenderTheme.h"
33 #include "core/rendering/RenderView.h"
34 #include "platform/text/LocaleToScriptMapping.h"
38 // FIXME: This scoping class is a short-term fix to minimize the changes in
39 // Font-constructing logic.
40 class FontDescriptionChangeScope {
42 FontDescriptionChangeScope(FontBuilder* fontBuilder)
43 : m_fontBuilder(fontBuilder)
44 , m_fontDescription(fontBuilder->m_style->fontDescription())
48 void reset() { m_fontDescription = FontDescription(); }
49 void set(const FontDescription& fontDescription) { m_fontDescription = fontDescription; }
50 FontDescription& fontDescription() { return m_fontDescription; }
52 ~FontDescriptionChangeScope()
54 m_fontBuilder->didChangeFontParameters(m_fontBuilder->m_style->setFontDescription(m_fontDescription));
58 FontBuilder* m_fontBuilder;
59 FontDescription m_fontDescription;
62 FontBuilder::FontBuilder()
64 , m_useSVGZoomRules(false)
65 , m_fontSizehasViewportUnits(false)
71 void FontBuilder::initForStyleResolve(const Document& document, RenderStyle* style, bool useSVGZoomRules)
73 // All documents need to be in a frame (and thus have access to Settings)
74 // for style-resolution to make sense.
75 // Unfortunately SVG Animations currently violate this: crbug.com/260966
76 // ASSERT(m_document->frame());
77 m_document = &document;
78 m_useSVGZoomRules = useSVGZoomRules;
83 inline static void setFontFamilyToStandard(FontDescription& fontDescription, const Document* document)
85 if (!document || !document->settings())
88 fontDescription.setGenericFamily(FontDescription::StandardFamily);
89 const AtomicString& standardFontFamily = document->settings()->genericFontFamilySettings().standard();
90 if (standardFontFamily.isEmpty())
93 fontDescription.firstFamily().setFamily(standardFontFamily);
94 // FIXME: Why is this needed here?
95 fontDescription.firstFamily().appendFamily(nullptr);
98 void FontBuilder::setInitial(float effectiveZoom)
100 ASSERT(m_document && m_document->settings());
101 if (!m_document || !m_document->settings())
104 FontDescriptionChangeScope scope(this);
107 scope.fontDescription().setUsePrinterFont(m_document->printing());
108 setFontFamilyToStandard(scope.fontDescription(), m_document);
109 scope.fontDescription().setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1);
110 setSize(scope.fontDescription(), effectiveZoom, FontSize::fontSizeForKeyword(m_document, CSSValueMedium, false));
113 void FontBuilder::inheritFrom(const FontDescription& fontDescription)
115 FontDescriptionChangeScope scope(this);
117 scope.set(fontDescription);
120 void FontBuilder::didChangeFontParameters(bool changed)
122 m_fontDirty |= changed;
125 void FontBuilder::fromSystemFont(CSSValueID valueId, float effectiveZoom)
127 FontDescriptionChangeScope scope(this);
129 FontDescription fontDescription;
130 RenderTheme::theme().systemFont(valueId, fontDescription);
132 // Double-check and see if the theme did anything. If not, don't bother updating the font.
133 if (!fontDescription.isAbsoluteSize())
136 // Make sure the rendering mode and printer font settings are updated.
137 const Settings* settings = m_document->settings();
138 ASSERT(settings); // If we're doing style resolution, this document should always be in a frame and thus have settings
141 fontDescription.setUsePrinterFont(m_document->printing());
143 // Handle the zoom factor.
144 fontDescription.setComputedSize(getComputedSizeFromSpecifiedSize(fontDescription, effectiveZoom, fontDescription.specifiedSize()));
145 scope.set(fontDescription);
148 void FontBuilder::setFontFamilyInitial()
150 FontDescriptionChangeScope scope(this);
152 setFontFamilyToStandard(scope.fontDescription(), m_document);
155 void FontBuilder::setFontFamilyInherit(const FontDescription& parentFontDescription)
157 FontDescriptionChangeScope scope(this);
159 scope.fontDescription().setGenericFamily(parentFontDescription.genericFamily());
160 scope.fontDescription().setFamily(parentFontDescription.family());
163 // FIXME: I am not convinced FontBuilder needs to know anything about CSSValues.
164 void FontBuilder::setFontFamilyValue(CSSValue* value)
166 FontDescriptionChangeScope scope(this);
168 if (!value->isValueList())
171 FontFamily& firstFamily = scope.fontDescription().firstFamily();
172 FontFamily* currFamily = 0;
174 // Before mapping in a new font-family property, we should reset the generic family.
175 bool oldFamilyUsedFixedDefaultSize = scope.fontDescription().useFixedDefaultSize();
176 scope.fontDescription().setGenericFamily(FontDescription::NoFamily);
178 for (CSSValueListIterator i = value; i.hasMore(); i.advance()) {
179 CSSValue* item = i.value();
180 if (!item->isPrimitiveValue())
182 CSSPrimitiveValue* contentValue = toCSSPrimitiveValue(item);
184 Settings* settings = m_document->settings();
185 if (contentValue->isString()) {
186 face = AtomicString(contentValue->getStringValue());
187 } else if (settings) {
188 switch (contentValue->getValueID()) {
189 case CSSValueWebkitBody:
190 face = settings->genericFontFamilySettings().standard();
193 face = FontFamilyNames::webkit_serif;
194 scope.fontDescription().setGenericFamily(FontDescription::SerifFamily);
196 case CSSValueSansSerif:
197 face = FontFamilyNames::webkit_sans_serif;
198 scope.fontDescription().setGenericFamily(FontDescription::SansSerifFamily);
200 case CSSValueCursive:
201 face = FontFamilyNames::webkit_cursive;
202 scope.fontDescription().setGenericFamily(FontDescription::CursiveFamily);
204 case CSSValueFantasy:
205 face = FontFamilyNames::webkit_fantasy;
206 scope.fontDescription().setGenericFamily(FontDescription::FantasyFamily);
208 case CSSValueMonospace:
209 face = FontFamilyNames::webkit_monospace;
210 scope.fontDescription().setGenericFamily(FontDescription::MonospaceFamily);
212 case CSSValueWebkitPictograph:
213 face = FontFamilyNames::webkit_pictograph;
214 scope.fontDescription().setGenericFamily(FontDescription::PictographFamily);
221 if (!face.isEmpty()) {
223 // Filling in the first family.
224 firstFamily.setFamily(face);
225 firstFamily.appendFamily(nullptr); // Remove any inherited family-fallback list.
226 currFamily = &firstFamily;
228 RefPtr<SharedFontFamily> newFamily = SharedFontFamily::create();
229 newFamily->setFamily(face);
230 currFamily->appendFamily(newFamily);
231 currFamily = newFamily.get();
236 // We can't call useFixedDefaultSize() until all new font families have been added
237 // If currFamily is non-zero then we set at least one family on this description.
241 if (scope.fontDescription().keywordSize() && scope.fontDescription().useFixedDefaultSize() != oldFamilyUsedFixedDefaultSize)
242 scope.fontDescription().setSpecifiedSize(FontSize::fontSizeForKeyword(m_document, CSSValueXxSmall + scope.fontDescription().keywordSize() - 1, !oldFamilyUsedFixedDefaultSize));
245 void FontBuilder::setFontSizeInitial()
247 FontDescriptionChangeScope scope(this);
249 float size = FontSize::fontSizeForKeyword(m_document, CSSValueMedium, scope.fontDescription().useFixedDefaultSize());
254 scope.fontDescription().setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1);
255 scope.fontDescription().setSpecifiedSize(size);
258 void FontBuilder::setFontSizeInherit(const FontDescription& parentFontDescription)
260 FontDescriptionChangeScope scope(this);
262 float size = parentFontDescription.specifiedSize();
267 scope.fontDescription().setKeywordSize(parentFontDescription.keywordSize());
268 scope.fontDescription().setSpecifiedSize(size);
271 // FIXME: Figure out where we fall in the size ranges (xx-small to xxx-large)
272 // and scale down/up to the next size level.
273 static float largerFontSize(float size)
278 static float smallerFontSize(float size)
283 // FIXME: Have to pass RenderStyles here for calc/computed values. This shouldn't be neecessary.
284 void FontBuilder::setFontSizeValue(CSSValue* value, RenderStyle* parentStyle, const RenderStyle* rootElementStyle)
286 if (!value->isPrimitiveValue())
289 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
291 FontDescriptionChangeScope scope(this);
293 scope.fontDescription().setKeywordSize(0);
294 float parentSize = 0;
295 bool parentIsAbsoluteSize = false;
298 // FIXME: Find out when parentStyle could be 0?
300 parentSize = parentStyle->fontDescription().specifiedSize();
301 parentIsAbsoluteSize = parentStyle->fontDescription().isAbsoluteSize();
304 if (CSSValueID valueID = primitiveValue->getValueID()) {
306 case CSSValueXxSmall:
312 case CSSValueXxLarge:
313 case CSSValueWebkitXxxLarge:
314 size = FontSize::fontSizeForKeyword(m_document, valueID, scope.fontDescription().useFixedDefaultSize());
315 scope.fontDescription().setKeywordSize(valueID - CSSValueXxSmall + 1);
318 size = largerFontSize(parentSize);
320 case CSSValueSmaller:
321 size = smallerFontSize(parentSize);
327 scope.fontDescription().setIsAbsoluteSize(parentIsAbsoluteSize && (valueID == CSSValueLarger || valueID == CSSValueSmaller));
329 scope.fontDescription().setIsAbsoluteSize(parentIsAbsoluteSize || !(primitiveValue->isPercentage() || primitiveValue->isFontRelativeLength()));
330 if (primitiveValue->isPercentage()) {
331 size = (primitiveValue->getFloatValue() * parentSize) / 100.0f;
333 // If we have viewport units the conversion will mark the parent style as having viewport units.
334 bool parentHasViewportUnits = parentStyle->hasViewportUnits();
335 parentStyle->setHasViewportUnits(false);
336 CSSToLengthConversionData conversionData(parentStyle, rootElementStyle, m_document->renderView(), 1.0f, true);
337 if (primitiveValue->isLength())
338 size = primitiveValue->computeLength<float>(conversionData);
339 else if (primitiveValue->isCalculatedPercentageWithLength())
340 size = primitiveValue->cssCalcValue()->toCalcValue(conversionData)->evaluate(parentSize);
342 ASSERT_NOT_REACHED();
343 m_fontSizehasViewportUnits = parentStyle->hasViewportUnits();
344 parentStyle->setHasViewportUnits(parentHasViewportUnits);
351 // Overly large font sizes will cause crashes on some platforms (such as Windows).
352 // Cap font size here to make sure that doesn't happen.
353 size = std::min(maximumAllowedFontSize, size);
356 scope.fontDescription().setSpecifiedSize(size);
359 void FontBuilder::setWeight(FontWeight fontWeight)
361 FontDescriptionChangeScope scope(this);
363 scope.fontDescription().setWeight(fontWeight);
366 void FontBuilder::setWeightBolder()
368 FontDescriptionChangeScope scope(this);
370 scope.fontDescription().setWeight(scope.fontDescription().bolderWeight());
373 void FontBuilder::setWeightLighter()
375 FontDescriptionChangeScope scope(this);
377 scope.fontDescription().setWeight(scope.fontDescription().lighterWeight());
380 void FontBuilder::setFontVariantLigaturesInitial()
382 FontDescriptionChangeScope scope(this);
384 scope.fontDescription().setCommonLigaturesState(FontDescription::NormalLigaturesState);
385 scope.fontDescription().setDiscretionaryLigaturesState(FontDescription::NormalLigaturesState);
386 scope.fontDescription().setHistoricalLigaturesState(FontDescription::NormalLigaturesState);
387 scope.fontDescription().setContextualLigaturesState(FontDescription::NormalLigaturesState);
390 void FontBuilder::setFontVariantLigaturesInherit(const FontDescription& parentFontDescription)
392 FontDescriptionChangeScope scope(this);
394 scope.fontDescription().setCommonLigaturesState(parentFontDescription.commonLigaturesState());
395 scope.fontDescription().setDiscretionaryLigaturesState(parentFontDescription.discretionaryLigaturesState());
396 scope.fontDescription().setHistoricalLigaturesState(parentFontDescription.historicalLigaturesState());
397 scope.fontDescription().setContextualLigaturesState(parentFontDescription.historicalLigaturesState());
400 void FontBuilder::setFontVariantLigaturesValue(CSSValue* value)
402 FontDescriptionChangeScope scope(this);
404 FontDescription::LigaturesState commonLigaturesState = FontDescription::NormalLigaturesState;
405 FontDescription::LigaturesState discretionaryLigaturesState = FontDescription::NormalLigaturesState;
406 FontDescription::LigaturesState historicalLigaturesState = FontDescription::NormalLigaturesState;
407 FontDescription::LigaturesState contextualLigaturesState = FontDescription::NormalLigaturesState;
409 if (value->isValueList()) {
410 CSSValueList* valueList = toCSSValueList(value);
411 for (size_t i = 0; i < valueList->length(); ++i) {
412 CSSValue* item = valueList->itemWithoutBoundsCheck(i);
413 ASSERT(item->isPrimitiveValue());
414 if (item->isPrimitiveValue()) {
415 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(item);
416 switch (primitiveValue->getValueID()) {
417 case CSSValueNoCommonLigatures:
418 commonLigaturesState = FontDescription::DisabledLigaturesState;
420 case CSSValueCommonLigatures:
421 commonLigaturesState = FontDescription::EnabledLigaturesState;
423 case CSSValueNoDiscretionaryLigatures:
424 discretionaryLigaturesState = FontDescription::DisabledLigaturesState;
426 case CSSValueDiscretionaryLigatures:
427 discretionaryLigaturesState = FontDescription::EnabledLigaturesState;
429 case CSSValueNoHistoricalLigatures:
430 historicalLigaturesState = FontDescription::DisabledLigaturesState;
432 case CSSValueHistoricalLigatures:
433 historicalLigaturesState = FontDescription::EnabledLigaturesState;
435 case CSSValueNoContextual:
436 contextualLigaturesState = FontDescription::DisabledLigaturesState;
438 case CSSValueContextual:
439 contextualLigaturesState = FontDescription::EnabledLigaturesState;
442 ASSERT_NOT_REACHED();
450 ASSERT_WITH_SECURITY_IMPLICATION(value->isPrimitiveValue());
451 ASSERT(toCSSPrimitiveValue(value)->getValueID() == CSSValueNormal);
455 scope.fontDescription().setCommonLigaturesState(commonLigaturesState);
456 scope.fontDescription().setDiscretionaryLigaturesState(discretionaryLigaturesState);
457 scope.fontDescription().setHistoricalLigaturesState(historicalLigaturesState);
458 scope.fontDescription().setContextualLigaturesState(contextualLigaturesState);
461 void FontBuilder::setScript(const String& locale)
463 FontDescriptionChangeScope scope(this);
465 scope.fontDescription().setScript(localeToScriptCodeForFontSelection(locale));
468 void FontBuilder::setStyle(FontStyle italic)
470 FontDescriptionChangeScope scope(this);
472 scope.fontDescription().setStyle(italic);
475 void FontBuilder::setVariant(FontVariant smallCaps)
477 FontDescriptionChangeScope scope(this);
479 scope.fontDescription().setVariant(smallCaps);
482 void FontBuilder::setTextRendering(TextRenderingMode textRenderingMode)
484 FontDescriptionChangeScope scope(this);
486 scope.fontDescription().setTextRendering(textRenderingMode);
489 void FontBuilder::setKerning(FontDescription::Kerning kerning)
491 FontDescriptionChangeScope scope(this);
493 scope.fontDescription().setKerning(kerning);
496 void FontBuilder::setFontSmoothing(FontSmoothingMode foontSmoothingMode)
498 FontDescriptionChangeScope scope(this);
500 scope.fontDescription().setFontSmoothing(foontSmoothingMode);
503 void FontBuilder::setFeatureSettingsNormal()
505 FontDescriptionChangeScope scope(this);
507 // FIXME: Eliminate FontDescription::makeNormalFeatureSettings. It's useless.
508 scope.set(scope.fontDescription().makeNormalFeatureSettings());
511 void FontBuilder::setFeatureSettingsValue(CSSValue* value)
513 FontDescriptionChangeScope scope(this);
515 CSSValueList* list = toCSSValueList(value);
516 RefPtr<FontFeatureSettings> settings = FontFeatureSettings::create();
517 int len = list->length();
518 for (int i = 0; i < len; ++i) {
519 CSSValue* item = list->itemWithoutBoundsCheck(i);
520 if (!item->isFontFeatureValue())
522 CSSFontFeatureValue* feature = toCSSFontFeatureValue(item);
523 settings->append(FontFeature(feature->tag(), feature->value()));
525 scope.fontDescription().setFeatureSettings(settings.release());
528 void FontBuilder::setSize(FontDescription& fontDescription, float effectiveZoom, float size)
530 fontDescription.setSpecifiedSize(size);
531 fontDescription.setComputedSize(getComputedSizeFromSpecifiedSize(fontDescription, effectiveZoom, size));
534 float FontBuilder::getComputedSizeFromSpecifiedSize(FontDescription& fontDescription, float effectiveZoom, float specifiedSize)
536 float zoomFactor = 1.0f;
537 if (!m_useSVGZoomRules) {
538 zoomFactor = effectiveZoom;
539 // FIXME: Why is this here!!!!?!
540 if (LocalFrame* frame = m_document->frame())
541 zoomFactor *= frame->textZoomFactor();
544 return FontSize::getComputedSizeFromSpecifiedSize(m_document, zoomFactor, fontDescription.isAbsoluteSize(), specifiedSize);
547 static void getFontAndGlyphOrientation(const RenderStyle* style, FontOrientation& fontOrientation, NonCJKGlyphOrientation& glyphOrientation)
549 if (style->isHorizontalWritingMode()) {
550 fontOrientation = Horizontal;
551 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
555 switch (style->textOrientation()) {
556 case TextOrientationVerticalRight:
557 fontOrientation = Vertical;
558 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
560 case TextOrientationUpright:
561 fontOrientation = Vertical;
562 glyphOrientation = NonCJKGlyphOrientationUpright;
564 case TextOrientationSideways:
565 if (style->writingMode() == LeftToRightWritingMode) {
566 // FIXME: This should map to sideways-left, which is not supported yet.
567 fontOrientation = Vertical;
568 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
571 fontOrientation = Horizontal;
572 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
574 case TextOrientationSidewaysRight:
575 fontOrientation = Horizontal;
576 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
579 ASSERT_NOT_REACHED();
580 fontOrientation = Horizontal;
581 glyphOrientation = NonCJKGlyphOrientationVerticalRight;
586 void FontBuilder::checkForOrientationChange(RenderStyle* style)
588 FontOrientation fontOrientation;
589 NonCJKGlyphOrientation glyphOrientation;
590 getFontAndGlyphOrientation(style, fontOrientation, glyphOrientation);
592 FontDescriptionChangeScope scope(this);
594 if (scope.fontDescription().orientation() == fontOrientation && scope.fontDescription().nonCJKGlyphOrientation() == glyphOrientation)
597 scope.fontDescription().setNonCJKGlyphOrientation(glyphOrientation);
598 scope.fontDescription().setOrientation(fontOrientation);
601 void FontBuilder::checkForGenericFamilyChange(RenderStyle* style, const RenderStyle* parentStyle)
603 FontDescriptionChangeScope scope(this);
605 if (scope.fontDescription().isAbsoluteSize() || !parentStyle)
608 const FontDescription& parentFontDescription = parentStyle->fontDescription();
609 if (scope.fontDescription().useFixedDefaultSize() == parentFontDescription.useFixedDefaultSize())
612 // For now, lump all families but monospace together.
613 if (scope.fontDescription().genericFamily() != FontDescription::MonospaceFamily
614 && parentFontDescription.genericFamily() != FontDescription::MonospaceFamily)
617 // We know the parent is monospace or the child is monospace, and that font
618 // size was unspecified. We want to scale our font size as appropriate.
619 // If the font uses a keyword size, then we refetch from the table rather than
620 // multiplying by our scale factor.
622 if (scope.fontDescription().keywordSize()) {
623 size = FontSize::fontSizeForKeyword(m_document, CSSValueXxSmall + scope.fontDescription().keywordSize() - 1, scope.fontDescription().useFixedDefaultSize());
625 Settings* settings = m_document->settings();
626 float fixedScaleFactor = (settings && settings->defaultFixedFontSize() && settings->defaultFontSize())
627 ? static_cast<float>(settings->defaultFixedFontSize()) / settings->defaultFontSize()
629 size = parentFontDescription.useFixedDefaultSize() ?
630 scope.fontDescription().specifiedSize() / fixedScaleFactor :
631 scope.fontDescription().specifiedSize() * fixedScaleFactor;
634 setSize(scope.fontDescription(), style->effectiveZoom(), size);
637 void FontBuilder::updateComputedSize(RenderStyle* style, const RenderStyle* parentStyle)
639 FontDescriptionChangeScope scope(this);
641 scope.fontDescription().setComputedSize(getComputedSizeFromSpecifiedSize(scope.fontDescription(), style->effectiveZoom(), scope.fontDescription().specifiedSize()));
644 // FIXME: style param should come first
645 void FontBuilder::createFont(PassRefPtr<FontSelector> fontSelector, const RenderStyle* parentStyle, RenderStyle* style)
650 updateComputedSize(style, parentStyle);
651 checkForGenericFamilyChange(style, parentStyle);
652 checkForOrientationChange(style);
653 style->font().update(fontSelector);
657 void FontBuilder::createFontForDocument(PassRefPtr<FontSelector> fontSelector, RenderStyle* documentStyle)
659 FontDescription fontDescription = FontDescription();
660 fontDescription.setScript(localeToScriptCodeForFontSelection(documentStyle->locale()));
661 fontDescription.setUsePrinterFont(m_document->printing());
663 setFontFamilyToStandard(fontDescription, m_document);
664 fontDescription.setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1);
665 int size = FontSize::fontSizeForKeyword(m_document, CSSValueMedium, false);
666 fontDescription.setSpecifiedSize(size);
667 fontDescription.setComputedSize(getComputedSizeFromSpecifiedSize(fontDescription, documentStyle->effectiveZoom(), size));
669 FontOrientation fontOrientation;
670 NonCJKGlyphOrientation glyphOrientation;
671 getFontAndGlyphOrientation(documentStyle, fontOrientation, glyphOrientation);
672 fontDescription.setOrientation(fontOrientation);
673 fontDescription.setNonCJKGlyphOrientation(glyphOrientation);
674 documentStyle->setFontDescription(fontDescription);
675 documentStyle->font().update(fontSelector);