| /* | 
 |  * Copyright (C) 2007 Nicholas Shanks <contact@nickshanks.com> | 
 |  * Copyright (C) 2008 Apple Inc. All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * | 
 |  * 1.  Redistributions of source code must retain the above copyright | 
 |  *     notice, this list of conditions and the following disclaimer. | 
 |  * 2.  Redistributions in binary form must reproduce the above copyright | 
 |  *     notice, this list of conditions and the following disclaimer in the | 
 |  *     documentation and/or other materials provided with the distribution. | 
 |  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of | 
 |  *     its contributors may be used to endorse or promote products derived | 
 |  *     from this software without specific prior written permission. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | 
 |  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 
 |  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
 |  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | 
 |  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 
 |  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 
 |  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 
 |  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
 |  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include "third_party/blink/renderer/platform/fonts/font_description.h" | 
 |  | 
 | #include <set> | 
 |  | 
 | #include "base/compiler_specific.h" | 
 | #include "base/memory/values_equivalent.h" | 
 | #include "base/notreached.h" | 
 | #include "base/strings/to_string.h" | 
 | #include "build/build_config.h" | 
 | #include "third_party/blink/public/platform/web_font_description.h" | 
 | #include "third_party/blink/renderer/platform/geometry/evaluation_input.h" | 
 | #include "third_party/blink/renderer/platform/language.h" | 
 | #include "third_party/blink/renderer/platform/wtf/hash_functions.h" | 
 | #include "third_party/blink/renderer/platform/wtf/size_assertions.h" | 
 | #include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h" | 
 | #include "third_party/blink/renderer/platform/wtf/text/string_hash.h" | 
 | #include "third_party/blink/renderer/platform/wtf/text/string_hasher.h" | 
 |  | 
 | #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) | 
 | #include "third_party/blink/renderer/platform/fonts/font_cache.h" | 
 | #endif | 
 |  | 
 | namespace blink { | 
 |  | 
 | struct SameSizeAsFontDescription { | 
 |   DISALLOW_NEW(); | 
 |   FontFamily family_list; | 
 |   scoped_refptr<FontFeatureSettings> feature_settings_; | 
 |   scoped_refptr<FontVariationSettings> variation_settings_; | 
 |   scoped_refptr<FontPalette> palette_; | 
 |   scoped_refptr<FontVariantAlternates> font_variant_alternates_; | 
 |   AtomicString locale; | 
 |   float sizes[3]; | 
 |   Length letter_spacing; | 
 |   Length word_spacing; | 
 |   FontSizeAdjust size_adjust_; | 
 |   ResolvedFontFeatures resolved_font_features_; | 
 |   FontSelectionRequest selection_request_; | 
 |   FieldsAsUnsignedType bitfields; | 
 |   AtomicString language_override_; | 
 | }; | 
 |  | 
 | ASSERT_SIZE(FontDescription, SameSizeAsFontDescription); | 
 |  | 
 | bool FontDescription::use_subpixel_text_positioning_ = false; | 
 |  | 
 | // static | 
 | FontDescription FontDescription::CreateHashTableEmptyValue() { | 
 |   FontDescription result; | 
 |   UNSAFE_TODO(memset(&result, 0, sizeof(FontDescription))); | 
 |   DCHECK(result.IsHashTableEmptyValue()); | 
 |   return result; | 
 | } | 
 |  | 
 | FontDescription::FontDescription(HashTableDeletedValueType) { | 
 |   UNSAFE_TODO(memset(this, 0, sizeof(FontDescription))); | 
 |   fields_.hash_category_ = kHashDeletedValue; | 
 | } | 
 |  | 
 | FontDescription::FontDescription() | 
 |     : specified_size_(0), | 
 |       computed_size_(0), | 
 |       adjusted_size_(0), | 
 |       letter_spacing_(Length(0, Length::kFixed)), | 
 |       word_spacing_(Length(0, Length::kFixed)), | 
 |       font_selection_request_(kNormalWeightValue, | 
 |                               kNormalWidthValue, | 
 |                               kNormalSlopeValue) { | 
 |   fields_.orientation_ = static_cast<unsigned>(FontOrientation::kHorizontal); | 
 |   fields_.width_variant_ = kRegularWidth; | 
 |   fields_.variant_caps_ = kCapsNormal; | 
 |   fields_.is_absolute_size_ = false; | 
 |   fields_.generic_family_ = kNoFamily; | 
 |   fields_.kerning_ = kAutoKerning; | 
 |   fields_.common_ligatures_state_ = kNormalLigaturesState; | 
 |   fields_.discretionary_ligatures_state_ = kNormalLigaturesState; | 
 |   fields_.historical_ligatures_state_ = kNormalLigaturesState; | 
 |   fields_.contextual_ligatures_state_ = kNormalLigaturesState; | 
 |   fields_.keyword_size_ = 0; | 
 |   fields_.font_smoothing_ = kAutoSmoothing; | 
 |   fields_.text_rendering_ = kAutoTextRendering; | 
 |   fields_.synthetic_bold_ = false; | 
 |   fields_.synthetic_italic_ = false; | 
 |   fields_.synthetic_oblique_ = false; | 
 |   fields_.subpixel_text_position_ = use_subpixel_text_positioning_; | 
 |   fields_.typesetting_features_ = 0; | 
 |   fields_.variant_numeric_ = FontVariantNumeric().fields_as_unsigned_; | 
 |   fields_.variant_east_asian_ = 0; | 
 |   fields_.subpixel_ascent_descent_ = false; | 
 |   fields_.font_optical_sizing_ = OpticalSizing::kAutoOpticalSizing; | 
 |   fields_.has_size_adjust_descriptor_ = false; | 
 |   fields_.hash_category_ = kHashRegularValue; | 
 |   fields_.font_synthesis_weight_ = kAutoFontSynthesisWeight; | 
 |   fields_.font_synthesis_style_ = kAutoFontSynthesisStyle; | 
 |   fields_.font_synthesis_small_caps_ = kAutoFontSynthesisSmallCaps; | 
 |   fields_.variant_position_ = kNormalVariantPosition; | 
 |   fields_.variant_emoji_ = kNormalVariantEmoji; | 
 |   fields_.text_spacing_trim_ = static_cast<unsigned>(TextSpacingTrim::kInitial); | 
 | } | 
 |  | 
 | FontDescription::FontDescription(const FontDescription&) = default; | 
 |  | 
 | FontDescription& FontDescription::operator=(const FontDescription&) = default; | 
 |  | 
 | bool FontDescription::operator==(const FontDescription& other) const { | 
 |   return family_list_ == other.family_list_ && locale_ == other.locale_ && | 
 |          specified_size_ == other.specified_size_ && | 
 |          computed_size_ == other.computed_size_ && | 
 |          adjusted_size_ == other.adjusted_size_ && | 
 |          size_adjust_ == other.size_adjust_ && | 
 |          letter_spacing_ == other.letter_spacing_ && | 
 |          word_spacing_ == other.word_spacing_ && | 
 |          font_selection_request_ == other.font_selection_request_ && | 
 |          fields_as_unsigned_.parts[0] == other.fields_as_unsigned_.parts[0] && | 
 |          fields_as_unsigned_.parts[1] == other.fields_as_unsigned_.parts[1] && | 
 |          (feature_settings_ == other.feature_settings_ || | 
 |           (feature_settings_ && other.feature_settings_ && | 
 |            *feature_settings_ == *other.feature_settings_)) && | 
 |          (variation_settings_ == other.variation_settings_ || | 
 |           (variation_settings_ && other.variation_settings_ && | 
 |            *variation_settings_ == *other.variation_settings_)) && | 
 |          base::ValuesEquivalent(font_palette_, other.font_palette_) && | 
 |          base::ValuesEquivalent(font_variant_alternates_, | 
 |                                 other.font_variant_alternates_); | 
 | } | 
 |  | 
 | // Compute a 'lighter' weight per | 
 | // https://drafts.csswg.org/css-fonts-4/#font-weight-prop | 
 | FontSelectionValue FontDescription::LighterWeight(FontSelectionValue weight) { | 
 |   DCHECK(weight >= FontSelectionValue(1) && weight <= FontSelectionValue(1000)); | 
 |   // [1, 100) => No change | 
 |   if (weight < FontSelectionValue(100)) | 
 |     return weight; | 
 |   // [100, 550) => 100 | 
 |   if (weight < FontSelectionValue(550)) | 
 |     return FontSelectionValue(100); | 
 |   // [550, 750) => 400 | 
 |   if (weight < FontSelectionValue(750)) | 
 |     return FontSelectionValue(400); | 
 |   // [750, 1000] => 700 | 
 |   return FontSelectionValue(700); | 
 | } | 
 |  | 
 | // Compute a 'bolder' weight per | 
 | // https://drafts.csswg.org/css-fonts-4/#font-weight-prop | 
 | FontSelectionValue FontDescription::BolderWeight(FontSelectionValue weight) { | 
 |   DCHECK(weight >= FontSelectionValue(1) && weight <= FontSelectionValue(1000)); | 
 |   // [1, 350) => 400 | 
 |   if (weight < FontSelectionValue(350)) | 
 |     return FontSelectionValue(400); | 
 |   // [350, 550) => 700 | 
 |   if (weight < FontSelectionValue(550)) | 
 |     return FontSelectionValue(700); | 
 |   // [550, 900) => 900 | 
 |   if (weight < FontSelectionValue(900)) | 
 |     return FontSelectionValue(900); | 
 |   // [900, 1000] => No change | 
 |   return weight; | 
 | } | 
 |  | 
 | FontDescription::Size FontDescription::LargerSize(const Size& size) { | 
 |   return Size(0, size.value * 1.2, size.is_absolute); | 
 | } | 
 |  | 
 | FontDescription::Size FontDescription::SmallerSize(const Size& size) { | 
 |   return Size(0, size.value / 1.2, size.is_absolute); | 
 | } | 
 |  | 
 | FontSelectionRequest FontDescription::GetFontSelectionRequest() const { | 
 |   return font_selection_request_; | 
 | } | 
 |  | 
 | float FontDescription::LetterSpacing() const { | 
 |   switch (letter_spacing_.GetType()) { | 
 |     case Length::kFixed: | 
 |       return letter_spacing_.Pixels(); | 
 |     case Length::kPercent: | 
 |       return letter_spacing_.Percent() / 100 * computed_size_; | 
 |     case Length::kCalculated: | 
 |       return letter_spacing_.NonNanCalculatedValue(LayoutUnit(computed_size_), | 
 |                                                    {}); | 
 |     default: | 
 |       NOTREACHED(); | 
 |   } | 
 | } | 
 |  | 
 | float FontDescription::WordSpacing() const { | 
 |   switch (word_spacing_.GetType()) { | 
 |     case Length::kFixed: | 
 |       return word_spacing_.Pixels(); | 
 |     case Length::kPercent: | 
 |       return word_spacing_.Percent() / 100 * computed_size_; | 
 |     case Length::kCalculated: | 
 |       return word_spacing_.NonNanCalculatedValue(LayoutUnit(computed_size_), | 
 |                                                  {}); | 
 |     default: | 
 |       NOTREACHED(); | 
 |   } | 
 | } | 
 |  | 
 | FontDescription::VariantLigatures FontDescription::GetVariantLigatures() const { | 
 |   VariantLigatures ligatures; | 
 |  | 
 |   ligatures.common = CommonLigaturesState(); | 
 |   ligatures.discretionary = DiscretionaryLigaturesState(); | 
 |   ligatures.historical = HistoricalLigaturesState(); | 
 |   ligatures.contextual = ContextualLigaturesState(); | 
 |  | 
 |   return ligatures; | 
 | } | 
 |  | 
 | void FontDescription::SetVariantCaps(FontVariantCaps variant_caps) { | 
 |   fields_.variant_caps_ = variant_caps; | 
 |  | 
 |   UpdateTypesettingFeatures(); | 
 | } | 
 |  | 
 | void FontDescription::SetVariantEastAsian( | 
 |     const FontVariantEastAsian variant_east_asian) { | 
 |   fields_.variant_east_asian_ = variant_east_asian.fields_as_unsigned_; | 
 | } | 
 |  | 
 | void FontDescription::SetVariantLigatures(const VariantLigatures& ligatures) { | 
 |   fields_.common_ligatures_state_ = ligatures.common; | 
 |   fields_.discretionary_ligatures_state_ = ligatures.discretionary; | 
 |   fields_.historical_ligatures_state_ = ligatures.historical; | 
 |   fields_.contextual_ligatures_state_ = ligatures.contextual; | 
 |  | 
 |   UpdateTypesettingFeatures(); | 
 | } | 
 |  | 
 | void FontDescription::SetVariantNumeric( | 
 |     const FontVariantNumeric& variant_numeric) { | 
 |   fields_.variant_numeric_ = variant_numeric.fields_as_unsigned_; | 
 |  | 
 |   UpdateTypesettingFeatures(); | 
 | } | 
 |  | 
 | float FontDescription::EffectiveFontSize() const { | 
 |   // Ensure that the effective precision matches the font-cache precision. | 
 |   // This guarantees that the same precision is used regardless of cache status. | 
 |   // Note: HasSizeAdjust() is for the font-size-adjust property, not for the | 
 |   // size-adjust descriptor. | 
 |   float computed_or_adjusted_size = | 
 |       HasSizeAdjust() || fields_.has_size_adjust_descriptor_ ? AdjustedSize() | 
 |                                                              : ComputedSize(); | 
 |   return floorf(computed_or_adjusted_size * | 
 |                 FontCacheKey::PrecisionMultiplier()) / | 
 |          FontCacheKey::PrecisionMultiplier(); | 
 | } | 
 |  | 
 | float FontDescription::AdjustedSpecifiedSize() const { | 
 |   if (HasSizeAdjust() || fields_.has_size_adjust_descriptor_) { | 
 |     return SpecifiedSize() * (AdjustedSize() / ComputedSize()); | 
 |   } else { | 
 |     return SpecifiedSize(); | 
 |   } | 
 | } | 
 |  | 
 | FontDescription FontDescription::SizeAdjustedFontDescription( | 
 |     float size_adjust) const { | 
 |   // See note in: https://www.w3.org/TR/css-fonts-5/#font-size-adjust-prop | 
 |   // When the font-size-adjust property is applied while a size-adjust | 
 |   // descriptor is set, the latter must not have an effect | 
 |   if (HasSizeAdjust()) | 
 |     return *this; | 
 |  | 
 |   // size-adjust should be applied at most once. | 
 |   DCHECK(!fields_.has_size_adjust_descriptor_); | 
 |   FontDescription result(*this); | 
 |   result.SetAdjustedSize(ComputedSize() * size_adjust); | 
 |   result.fields_.has_size_adjust_descriptor_ = true; | 
 |   return result; | 
 | } | 
 |  | 
 | FontCacheKey FontDescription::CacheKey( | 
 |     const FontFaceCreationParams& creation_params, | 
 |     bool is_unique_match) const { | 
 |   unsigned options = | 
 |       static_cast<unsigned>(fields_.variant_emoji_) << 10 |         // bit 11-12 | 
 |       static_cast<unsigned>(fields_.font_synthesis_style_) << 9 |   // bit 10 | 
 |       static_cast<unsigned>(fields_.font_synthesis_weight_) << 8 |  // bit 9 | 
 |       static_cast<unsigned>(fields_.font_optical_sizing_) << 7 |    // bit 8 | 
 |       static_cast<unsigned>(fields_.synthetic_italic_) << 6 |       // bit 7 | 
 |       static_cast<unsigned>(fields_.synthetic_bold_) << 5 |         // bit 6 | 
 |       static_cast<unsigned>(fields_.text_rendering_) << 3 |         // bits 4-5 | 
 |       static_cast<unsigned>(fields_.orientation_) << 1 |            // bit 2-3 | 
 |       static_cast<unsigned>(fields_.subpixel_text_position_);       // bit 1 | 
 |  | 
 | #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | 
 |   float device_scale_factor_for_key = FontCache::DeviceScaleFactor(); | 
 | #else | 
 |   float device_scale_factor_for_key = 1.0f; | 
 | #endif | 
 |   FontCacheKey cache_key(creation_params, EffectiveFontSize(), | 
 |                          options | font_selection_request_.GetHash() << 13, | 
 |                          device_scale_factor_for_key, size_adjust_, | 
 |                          variation_settings_, font_palette_, | 
 |                          font_variant_alternates_, is_unique_match); | 
 | #if BUILDFLAG(IS_ANDROID) | 
 |   if (const LayoutLocale* locale = Locale()) { | 
 |     if (FontCache::GetLocaleSpecificFamilyName(creation_params.Family())) | 
 |       cache_key.SetLocale(AtomicString(locale->LocaleForSkFontMgr())); | 
 |   } | 
 | #endif  // BUILDFLAG(IS_ANDROID) | 
 |   return cache_key; | 
 | } | 
 |  | 
 | void FontDescription::UpdateTypesettingFeatures() { | 
 |   fields_.typesetting_features_ = 0; | 
 |  | 
 |   switch (TextRendering()) { | 
 |     case kAutoTextRendering: | 
 |       break; | 
 |     case kOptimizeSpeed: | 
 |       fields_.typesetting_features_ &= ~(blink::kKerning | kLigatures); | 
 |       break; | 
 |     case kGeometricPrecision: | 
 |     case kOptimizeLegibility: | 
 |       fields_.typesetting_features_ |= blink::kKerning | kLigatures; | 
 |       break; | 
 |   } | 
 |  | 
 |   switch (GetKerning()) { | 
 |     case FontDescription::kNoneKerning: | 
 |       fields_.typesetting_features_ &= ~blink::kKerning; | 
 |       break; | 
 |     case FontDescription::kNormalKerning: | 
 |       fields_.typesetting_features_ |= blink::kKerning; | 
 |       break; | 
 |     case FontDescription::kAutoKerning: | 
 |       break; | 
 |   } | 
 |  | 
 |   // As per CSS (https://drafts.csswg.org/css-text/#letter-spacing-property), | 
 |   // When the effective letter-spacing between two characters is not zero (due | 
 |   // to either justification or non-zero computed letter-spacing), user agents | 
 |   // should not apply optional ligatures. | 
 |   if (letter_spacing_.IsZero()) { | 
 |     switch (CommonLigaturesState()) { | 
 |       case FontDescription::kDisabledLigaturesState: | 
 |         fields_.typesetting_features_ &= ~blink::kLigatures; | 
 |         break; | 
 |       case FontDescription::kEnabledLigaturesState: | 
 |         fields_.typesetting_features_ |= blink::kLigatures; | 
 |         break; | 
 |       case FontDescription::kNormalLigaturesState: | 
 |         break; | 
 |     } | 
 |  | 
 |     if (DiscretionaryLigaturesState() == | 
 |             FontDescription::kEnabledLigaturesState || | 
 |         HistoricalLigaturesState() == FontDescription::kEnabledLigaturesState || | 
 |         ContextualLigaturesState() == FontDescription::kEnabledLigaturesState) { | 
 |       fields_.typesetting_features_ |= blink::kLigatures; | 
 |     } | 
 |   } | 
 |  | 
 |   if (VariantCaps() != kCapsNormal) | 
 |     fields_.typesetting_features_ |= blink::kCaps; | 
 | } | 
 |  | 
 | unsigned FontDescription::StyleHashWithoutFamilyList() const { | 
 |   unsigned hash = 0; | 
 |   const FontFeatureSettings* settings = FeatureSettings(); | 
 |   if (settings) { | 
 |     unsigned num_features = settings->size(); | 
 |     for (unsigned i = 0; i < num_features; ++i) { | 
 |       AddIntToHash(hash, settings->at(i).Tag()); | 
 |       AddIntToHash(hash, settings->at(i).Value()); | 
 |     } | 
 |   } | 
 |  | 
 |   if (VariationSettings()) { | 
 |     AddIntToHash(hash, VariationSettings()->GetHash()); | 
 |   } | 
 |  | 
 |   if (HasLanguageOverride()) { | 
 |     AddIntToHash(hash, language_override_.Hash()); | 
 |   } | 
 |  | 
 |   if (font_palette_) { | 
 |     AddIntToHash(hash, font_palette_->GetHash()); | 
 |   } | 
 |  | 
 |   if (locale_) { | 
 |     const AtomicString& locale = locale_->LocaleString(); | 
 |     AddIntToHash(hash, locale.Hash()); | 
 |   } | 
 |  | 
 |   AddFloatToHash(hash, specified_size_); | 
 |   AddFloatToHash(hash, computed_size_); | 
 |   AddFloatToHash(hash, adjusted_size_); | 
 |   AddIntToHash(hash, letter_spacing_.GetHash()); | 
 |   AddIntToHash(hash, word_spacing_.GetHash()); | 
 |   AddIntToHash(hash, fields_as_unsigned_.parts[0]); | 
 |   AddIntToHash(hash, fields_as_unsigned_.parts[1]); | 
 |   AddIntToHash(hash, font_selection_request_.GetHash()); | 
 |   AddIntToHash(hash, size_adjust_.GetHash()); | 
 |  | 
 |   return hash; | 
 | } | 
 |  | 
 | unsigned FontDescription::GetHash() const { | 
 |   unsigned hash = StyleHashWithoutFamilyList(); | 
 |   for (const FontFamily* family = &family_list_; family; | 
 |        family = family->Next()) { | 
 |     if (family->FamilyName().empty()) | 
 |       continue; | 
 |     AddIntToHash(hash, family->FamilyIsGeneric()); | 
 |     AddIntToHash(hash, blink::GetHash(family->FamilyName())); | 
 |   } | 
 |   return hash; | 
 | } | 
 |  | 
 | void FontDescription::SetOrientation(FontOrientation orientation) { | 
 |   fields_.orientation_ = static_cast<unsigned>(orientation); | 
 |   UpdateSyntheticOblique(); | 
 | } | 
 |  | 
 | void FontDescription::SetStyle(FontSelectionValue value) { | 
 |   font_selection_request_.slope = value; | 
 |   original_slope = value; | 
 |   UpdateSyntheticOblique(); | 
 | } | 
 |  | 
 | void FontDescription::UpdateSyntheticOblique() { | 
 |   // Doing synthetic oblique for vertical writing mode with upright text | 
 |   // orientation when negative angle parameter of "oblique" keyword, e.g. | 
 |   // "font-style: oblique -15deg" for simulating "tts:fontShear"[1][2], we | 
 |   // need to have normal font style instead of italic/oblique. | 
 |   // [1] | 
 |   // https://www.w3.org/TR/2018/REC-ttml2-20181108/#style-attribute-fontShear | 
 |   // [2] See http://crbug.com/1112923 | 
 |   fields_.synthetic_oblique_ = | 
 |       IsVerticalAnyUpright() && original_slope < FontSelectionValue(0); | 
 |   font_selection_request_.slope = | 
 |       fields_.synthetic_oblique_ ? kNormalSlopeValue : original_slope; | 
 | } | 
 |  | 
 | SkFontStyle FontDescription::SkiaFontStyle() const { | 
 |   // FIXME(drott): This is a lossy conversion, compare | 
 |   // https://bugs.chromium.org/p/skia/issues/detail?id=6844 | 
 |   int skia_width = SkFontStyle::kNormal_Width; | 
 |   if (Stretch() <= kUltraCondensedWidthValue) { | 
 |     skia_width = SkFontStyle::kUltraCondensed_Width; | 
 |   } | 
 |   if (Stretch() <= kExtraCondensedWidthValue) { | 
 |     skia_width = SkFontStyle::kExtraCondensed_Width; | 
 |   } | 
 |   if (Stretch() <= kCondensedWidthValue) { | 
 |     skia_width = SkFontStyle::kCondensed_Width; | 
 |   } | 
 |   if (Stretch() <= kSemiCondensedWidthValue) { | 
 |     skia_width = SkFontStyle::kSemiCondensed_Width; | 
 |   } | 
 |   if (Stretch() >= kSemiExpandedWidthValue) { | 
 |     skia_width = SkFontStyle::kSemiExpanded_Width; | 
 |   } | 
 |   if (Stretch() >= kExpandedWidthValue) { | 
 |     skia_width = SkFontStyle::kExpanded_Width; | 
 |   } | 
 |   if (Stretch() >= kExtraExpandedWidthValue) { | 
 |     skia_width = SkFontStyle::kExtraExpanded_Width; | 
 |   } | 
 |   if (Stretch() >= kUltraExpandedWidthValue) { | 
 |     skia_width = SkFontStyle::kUltraExpanded_Width; | 
 |   } | 
 |  | 
 |   SkFontStyle::Slant slant = SkFontStyle::kUpright_Slant; | 
 |   FontSelectionValue style = Style(); | 
 |   if (style > kNormalSlopeValue && style <= kItalicThreshold) { | 
 |     slant = SkFontStyle::kItalic_Slant; | 
 |   } | 
 |   if (style > kItalicThreshold) { | 
 |     slant = SkFontStyle::kOblique_Slant; | 
 |   } | 
 |  | 
 |   int skia_weight = SkFontStyle::kNormal_Weight; | 
 |   if (Weight() >= kMinWeightValue && Weight() <= kMaxWeightValue) { | 
 |     skia_weight = static_cast<int>(Weight()); | 
 |   } | 
 |  | 
 |   return SkFontStyle(skia_weight, skia_width, slant); | 
 | } | 
 |  | 
 | void FontDescription::UpdateFromSkiaFontStyle(const SkFontStyle& font_style) { | 
 |   SetWeight(FontSelectionValue(font_style.weight())); | 
 |  | 
 |   switch (font_style.width()) { | 
 |     case (SkFontStyle::kUltraCondensed_Width): | 
 |       SetStretch(kUltraCondensedWidthValue); | 
 |       break; | 
 |     case (SkFontStyle::kExtraCondensed_Width): | 
 |       SetStretch(kExtraCondensedWidthValue); | 
 |       break; | 
 |     case (SkFontStyle::kCondensed_Width): | 
 |       SetStretch(kCondensedWidthValue); | 
 |       break; | 
 |     case (SkFontStyle::kSemiCondensed_Width): | 
 |       SetStretch(kSemiCondensedWidthValue); | 
 |       break; | 
 |     case (SkFontStyle::kSemiExpanded_Width): | 
 |       SetStretch(kSemiExpandedWidthValue); | 
 |       break; | 
 |     case (SkFontStyle::kExpanded_Width): | 
 |       SetStretch(kExpandedWidthValue); | 
 |       break; | 
 |     case (SkFontStyle::kExtraExpanded_Width): | 
 |       SetStretch(kExtraExpandedWidthValue); | 
 |       break; | 
 |     case (SkFontStyle::kUltraExpanded_Width): | 
 |       SetStretch(kUltraExpandedWidthValue); | 
 |       break; | 
 |   } | 
 |  | 
 |   if (font_style.slant() == SkFontStyle::kOblique_Slant) | 
 |     SetStyle(kItalicSlopeValue); | 
 |   else | 
 |     SetStyle(kNormalSlopeValue); | 
 | } | 
 |  | 
 | int FontDescription::MinimumPrefixWidthToHyphenate() const { | 
 |   // If the maximum width available for the prefix before the hyphen is small, | 
 |   // then it is very unlikely that an hyphenation opportunity exists, so do not | 
 |   // bother to look for it.  These are heuristic numbers for performance added | 
 |   // in http://wkb.ug/45606 | 
 |   const int kMinimumPrefixWidthNumerator = 5; | 
 |   const int kMinimumPrefixWidthDenominator = 4; | 
 |   return ComputedPixelSize() * kMinimumPrefixWidthNumerator / | 
 |          kMinimumPrefixWidthDenominator; | 
 | } | 
 |  | 
 | ResolvedFontFeatures FontDescription::ResolveFontFeatures() const { | 
 |   if (const auto* alternates = GetFontVariantAlternates()) { | 
 |     ResolvedFontFeatures features_with_description = | 
 |         alternates->GetResolvedFontFeatures(); | 
 |     features_with_description.AppendVector(resolved_font_features_); | 
 |     return features_with_description; | 
 |   } | 
 |   return resolved_font_features_; | 
 | } | 
 |  | 
 | void FontDescription::MergeFontFeatureSettingsWithDescriptor( | 
 |     const FontFeatureSettings* feature_settings_descriptor) { | 
 |   ResolvedFontFeatures resolved_font_features = | 
 |       ResolveFontFeatureSettingsDescriptor(FeatureSettings(), | 
 |                                            feature_settings_descriptor); | 
 |   SetResolvedFontFeatures(std::move(resolved_font_features)); | 
 | } | 
 |  | 
 | void FontDescription::MergeFontVariationSettingsWithDescriptor( | 
 |     const FontVariationSettings* variation_settings_descriptor) { | 
 |   scoped_refptr<FontVariationSettings> font_variation_settings = | 
 |       FontVariationSettings::Create(); | 
 |  | 
 |   if ((!variation_settings_ || variation_settings_->size() == 0) && | 
 |       (!variation_settings_descriptor || | 
 |        variation_settings_descriptor->size() == 0)) { | 
 |     SetVariationSettings(font_variation_settings); | 
 |     return; | 
 |   } | 
 |  | 
 |   std::set<uint32_t> existing_tags; | 
 |   // Store the existing axis settings | 
 |   if (variation_settings_) { | 
 |     for (const FontVariationAxis& axis : *variation_settings_) { | 
 |       existing_tags.insert(axis.Tag()); | 
 |       font_variation_settings->Append(axis); | 
 |     } | 
 |   } | 
 |   for (const FontVariationAxis& axis : *variation_settings_descriptor) { | 
 |     if (existing_tags.find(axis.Tag()) == existing_tags.end()) { | 
 |       font_variation_settings->Append( | 
 |           FontVariationAxis(axis.Tag(), axis.Value())); | 
 |     } | 
 |   } | 
 |   std::sort(font_variation_settings->begin(), font_variation_settings->end()); | 
 |   SetVariationSettings(font_variation_settings); | 
 | } | 
 |  | 
 | String FontDescription::ToString(GenericFamilyType familyType) { | 
 |   switch (familyType) { | 
 |     case GenericFamilyType::kNoFamily: | 
 |       return "None"; | 
 |     case GenericFamilyType::kStandardFamily: | 
 |       return "Standard"; | 
 |     case GenericFamilyType::kWebkitBodyFamily: | 
 |       return "WebkitBody"; | 
 |     case GenericFamilyType::kSerifFamily: | 
 |       return "Serif"; | 
 |     case GenericFamilyType::kSansSerifFamily: | 
 |       return "SansSerif"; | 
 |     case GenericFamilyType::kMonospaceFamily: | 
 |       return "Monospace"; | 
 |     case GenericFamilyType::kCursiveFamily: | 
 |       return "Cursive"; | 
 |     case GenericFamilyType::kFantasyFamily: | 
 |       return "Fantasy"; | 
 |   } | 
 |   return "Unknown"; | 
 | } | 
 |  | 
 | String FontDescription::ToString(LigaturesState state) { | 
 |   switch (state) { | 
 |     case LigaturesState::kNormalLigaturesState: | 
 |       return "Normal"; | 
 |     case LigaturesState::kDisabledLigaturesState: | 
 |       return "Disabled"; | 
 |     case LigaturesState::kEnabledLigaturesState: | 
 |       return "Enabled"; | 
 |   } | 
 |   return "Unknown"; | 
 | } | 
 |  | 
 | String FontDescription::ToString(Kerning kerning) { | 
 |   switch (kerning) { | 
 |     case Kerning::kAutoKerning: | 
 |       return "Auto"; | 
 |     case Kerning::kNormalKerning: | 
 |       return "Normal"; | 
 |     case Kerning::kNoneKerning: | 
 |       return "None"; | 
 |   } | 
 |   return "Unknown"; | 
 | } | 
 |  | 
 | String FontDescription::ToString(FontSelectionValue selection_value) { | 
 |   if (selection_value == kUltraCondensedWidthValue) { | 
 |     return "Ultra-Condensed"; | 
 |   } else if (selection_value == kExtraCondensedWidthValue) { | 
 |     return "Extra-Condensed"; | 
 |   } else if (selection_value == kCondensedWidthValue) { | 
 |     return "Condensed"; | 
 |   } else if (selection_value == kSemiCondensedWidthValue) { | 
 |     return "Semi-Condensed"; | 
 |   } else if (selection_value == kNormalWidthValue) { | 
 |     return "Normal"; | 
 |   } else if (selection_value == kSemiExpandedWidthValue) { | 
 |     return "Semi-Expanded"; | 
 |   } else if (selection_value == kExpandedWidthValue) { | 
 |     return "Expanded"; | 
 |   } else if (selection_value == kExtraExpandedWidthValue) { | 
 |     return "Extra-Expanded"; | 
 |   } else if (selection_value == kUltraExpandedWidthValue) { | 
 |     return "Ultra-Expanded"; | 
 |   } | 
 |  | 
 |   return "Unknown"; | 
 | } | 
 |  | 
 | String FontDescription::ToString(FontVariantCaps variant) { | 
 |   switch (variant) { | 
 |     case FontVariantCaps::kCapsNormal: | 
 |       return "Normal"; | 
 |     case FontVariantCaps::kSmallCaps: | 
 |       return "SmallCaps"; | 
 |     case FontVariantCaps::kAllSmallCaps: | 
 |       return "AllSmallCaps"; | 
 |     case FontVariantCaps::kPetiteCaps: | 
 |       return "PetiteCaps"; | 
 |     case FontVariantCaps::kAllPetiteCaps: | 
 |       return "AllPetiteCaps"; | 
 |     case FontVariantCaps::kUnicase: | 
 |       return "Unicase"; | 
 |     case FontVariantCaps::kTitlingCaps: | 
 |       return "TitlingCaps"; | 
 |   } | 
 |   return "Unknown"; | 
 | } | 
 |  | 
 | String FontDescription::ToStringForIdl(FontVariantCaps variant) { | 
 |   switch (variant) { | 
 |     case FontVariantCaps::kCapsNormal: | 
 |       return "normal"; | 
 |     case FontVariantCaps::kSmallCaps: | 
 |       return "small-caps"; | 
 |     case FontVariantCaps::kAllSmallCaps: | 
 |       return "all-small-caps"; | 
 |     case FontVariantCaps::kPetiteCaps: | 
 |       return "petite-caps"; | 
 |     case FontVariantCaps::kAllPetiteCaps: | 
 |       return "all-petite-caps"; | 
 |     case FontVariantCaps::kUnicase: | 
 |       return "unicase"; | 
 |     case FontVariantCaps::kTitlingCaps: | 
 |       return "titling-caps"; | 
 |   } | 
 |   return "Unknown"; | 
 | } | 
 |  | 
 | String FontDescription::ToString(FontSynthesisWeight font_synthesis_weight) { | 
 |   switch (font_synthesis_weight) { | 
 |     case FontSynthesisWeight::kAutoFontSynthesisWeight: | 
 |       return "Auto"; | 
 |     case FontSynthesisWeight::kNoneFontSynthesisWeight: | 
 |       return "None"; | 
 |   } | 
 |   return "Unknown"; | 
 | } | 
 |  | 
 | String FontDescription::ToString(FontSynthesisStyle font_synthesis_style) { | 
 |   switch (font_synthesis_style) { | 
 |     case FontSynthesisStyle::kAutoFontSynthesisStyle: | 
 |       return "Auto"; | 
 |     case FontSynthesisStyle::kNoneFontSynthesisStyle: | 
 |       return "None"; | 
 |   } | 
 |   return "Unknown"; | 
 | } | 
 |  | 
 | String FontDescription::ToString( | 
 |     FontSynthesisSmallCaps font_synthesis_small_caps) { | 
 |   switch (font_synthesis_small_caps) { | 
 |     case FontSynthesisSmallCaps::kAutoFontSynthesisSmallCaps: | 
 |       return "Auto"; | 
 |     case FontSynthesisSmallCaps::kNoneFontSynthesisSmallCaps: | 
 |       return "None"; | 
 |   } | 
 |   return "Unknown"; | 
 | } | 
 |  | 
 | String FontDescription::VariantLigatures::ToString() const { | 
 |   return String::Format( | 
 |       "common=%s, discretionary=%s, historical=%s, contextual=%s", | 
 |       FontDescription::ToString(static_cast<LigaturesState>(common)) | 
 |           .Ascii() | 
 |           .data(), | 
 |       FontDescription::ToString(static_cast<LigaturesState>(discretionary)) | 
 |           .Ascii() | 
 |           .data(), | 
 |       FontDescription::ToString(static_cast<LigaturesState>(historical)) | 
 |           .Ascii() | 
 |           .data(), | 
 |       FontDescription::ToString(static_cast<LigaturesState>(contextual)) | 
 |           .Ascii() | 
 |           .data()); | 
 | } | 
 |  | 
 | String FontDescription::Size::ToString() const { | 
 |   return String::Format( | 
 |       "keyword_size=%u, specified_size=%f, is_absolute_size=%s", keyword, value, | 
 |       base::ToString(is_absolute).c_str()); | 
 | } | 
 |  | 
 | String FontDescription::FamilyDescription::ToString() const { | 
 |   return String::Format( | 
 |       "generic_family=%s, family=[%s]", | 
 |       FontDescription::ToString(generic_family).Ascii().c_str(), | 
 |       family.ToString().Ascii().c_str()); | 
 | } | 
 |  | 
 | String FontDescription::ToString(FontVariantPosition variant_position) { | 
 |   switch (variant_position) { | 
 |     case FontVariantPosition::kNormalVariantPosition: | 
 |       return "Normal"; | 
 |     case FontVariantPosition::kSubVariantPosition: | 
 |       return "Sub"; | 
 |     case FontVariantPosition::kSuperVariantPosition: | 
 |       return "Super"; | 
 |   } | 
 |   return "Unknown"; | 
 | } | 
 |  | 
 | String FontDescription::ToString() const { | 
 |   return String::Format( | 
 |       "family_list=[%s], feature_settings=[%s], variation_settings=[%s], " | 
 |       "locale=%s, " | 
 |       "specified_size=%f, computed_size=%f, adjusted_size=%f, " | 
 |       "size_adjust=%s, letter_spacing=%f, word_spacing=%f, " | 
 |       "font_selection_request=[%s], " | 
 |       "typesetting_features=[%s], " | 
 |       "orientation=%s, width_variant=%s, variant_caps=%s, " | 
 |       "is_absolute_size=%s, generic_family=%s, kerning=%s, " | 
 |       "variant_ligatures=[%s], " | 
 |       "keyword_size=%u, font_smoothing=%s, text_rendering=%s, " | 
 |       "synthetic_bold=%s, synthetic_italic=%s, subpixel_positioning=%s, " | 
 |       "subpixel_ascent_descent=%s, variant_numeric=[%s], " | 
 |       "variant_east_asian=[%s], font_optical_sizing=%s, " | 
 |       "font_synthesis_weight=%s, font_synthesis_style=%s, " | 
 |       "font_synthesis_small_caps=%s, font_variant_position=%s, " | 
 |       "font_variant_emoji=%s", | 
 |       family_list_.ToString().Ascii().c_str(), | 
 |       (feature_settings_ ? feature_settings_->ToString().Ascii().c_str() : ""), | 
 |       (variation_settings_ ? variation_settings_->ToString().Ascii().c_str() | 
 |                            : ""), | 
 |       // TODO(wkorman): Locale has additional internal fields such as | 
 |       // hyphenation and script. Consider adding a more detailed | 
 |       // string method. | 
 |       (locale_ ? locale_->LocaleString().Ascii().c_str() : ""), specified_size_, | 
 |       computed_size_, adjusted_size_, size_adjust_.ToString().Ascii().c_str(), | 
 |       LetterSpacing(), WordSpacing(), | 
 |       font_selection_request_.ToString().Ascii().c_str(), | 
 |       blink::ToString( | 
 |           static_cast<TypesettingFeatures>(fields_.typesetting_features_)) | 
 |           .Ascii() | 
 |           .data(), | 
 |       blink::ToString(Orientation()).Ascii().c_str(), | 
 |       blink::ToString(WidthVariant()).Ascii().c_str(), | 
 |       FontDescription::ToString(VariantCaps()).Ascii().c_str(), | 
 |       base::ToString(IsAbsoluteSize()).c_str(), | 
 |       FontDescription::ToString(GenericFamily()).Ascii().c_str(), | 
 |       FontDescription::ToString(Kerning()).Ascii().c_str(), | 
 |       GetVariantLigatures().ToString().Ascii().c_str(), KeywordSize(), | 
 |       blink::ToString(FontSmoothing()).Ascii().c_str(), | 
 |       blink::ToString(TextRendering()).Ascii().c_str(), | 
 |       base::ToString(IsSyntheticBold()).c_str(), | 
 |       base::ToString(IsSyntheticItalic()).c_str(), | 
 |       base::ToString(UseSubpixelPositioning()).c_str(), | 
 |       base::ToString(SubpixelAscentDescent()).c_str(), | 
 |       VariantNumeric().ToString().Ascii().c_str(), | 
 |       VariantEastAsian().ToString().Ascii().c_str(), | 
 |       blink::ToString(FontOpticalSizing()).Ascii().c_str(), | 
 |       FontDescription::ToString(GetFontSynthesisWeight()).Ascii().c_str(), | 
 |       FontDescription::ToString(GetFontSynthesisStyle()).Ascii().c_str(), | 
 |       FontDescription::ToString(GetFontSynthesisSmallCaps()).Ascii().c_str(), | 
 |       FontDescription::ToString(VariantPosition()).Ascii().c_str(), | 
 |       blink::ToString(VariantEmoji()).Ascii().c_str()); | 
 | } | 
 |  | 
 | }  // namespace blink |