| /* |
| * 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 "platform/fonts/FontDescription.h" |
| |
| #include "platform/Language.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| #include "platform/wtf/StringHasher.h" |
| #include "platform/wtf/text/AtomicStringHash.h" |
| #include "platform/wtf/text/StringHash.h" |
| |
| namespace blink { |
| |
| struct SameSizeAsFontDescription { |
| DISALLOW_NEW(); |
| FontFamily family_list; |
| RefPtr<FontFeatureSettings> feature_settings_; |
| RefPtr<FontVariationSettings> variation_settings_; |
| AtomicString locale; |
| float sizes[6]; |
| FieldsAsUnsignedType bitfields; |
| }; |
| |
| static_assert(sizeof(FontDescription) == sizeof(SameSizeAsFontDescription), |
| "FontDescription should stay small"); |
| |
| TypesettingFeatures FontDescription::default_typesetting_features_ = 0; |
| |
| bool FontDescription::use_subpixel_text_positioning_ = false; |
| |
| FontDescription::FontDescription() |
| : specified_size_(0), |
| computed_size_(0), |
| adjusted_size_(0), |
| size_adjust_(kFontSizeAdjustNone), |
| letter_spacing_(0), |
| word_spacing_(0) { |
| fields_as_unsigned_.parts[0] = 0; |
| fields_as_unsigned_.parts[1] = 0; |
| fields_.orientation_ = static_cast<unsigned>(FontOrientation::kHorizontal); |
| fields_.width_variant_ = kRegularWidth; |
| fields_.style_ = kFontStyleNormal; |
| fields_.variant_caps_ = kCapsNormal; |
| fields_.is_absolute_size_ = false; |
| fields_.weight_ = kFontWeightNormal; |
| fields_.stretch_ = kFontStretchNormal; |
| 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_.subpixel_text_position_ = use_subpixel_text_positioning_; |
| fields_.typesetting_features_ = default_typesetting_features_; |
| fields_.variant_numeric_ = FontVariantNumeric().fields_as_unsigned_; |
| fields_.subpixel_ascent_descent_ = false; |
| } |
| |
| 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_ && |
| 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_)); |
| } |
| |
| FontWeight FontDescription::LighterWeight(FontWeight weight) { |
| switch (weight) { |
| case kFontWeight100: |
| case kFontWeight200: |
| case kFontWeight300: |
| case kFontWeight400: |
| case kFontWeight500: |
| return kFontWeight100; |
| |
| case kFontWeight600: |
| case kFontWeight700: |
| return kFontWeight400; |
| |
| case kFontWeight800: |
| case kFontWeight900: |
| return kFontWeight700; |
| } |
| NOTREACHED(); |
| return kFontWeightNormal; |
| } |
| |
| FontWeight FontDescription::BolderWeight(FontWeight weight) { |
| switch (weight) { |
| case kFontWeight100: |
| case kFontWeight200: |
| case kFontWeight300: |
| return kFontWeight400; |
| |
| case kFontWeight400: |
| case kFontWeight500: |
| return kFontWeight700; |
| |
| case kFontWeight600: |
| case kFontWeight700: |
| case kFontWeight800: |
| case kFontWeight900: |
| return kFontWeight900; |
| } |
| NOTREACHED(); |
| return kFontWeightNormal; |
| } |
| |
| 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); |
| } |
| |
| FontTraits FontDescription::Traits() const { |
| return FontTraits(Style(), Weight(), Stretch()); |
| } |
| |
| FontDescription::VariantLigatures FontDescription::GetVariantLigatures() const { |
| VariantLigatures ligatures; |
| |
| ligatures.common = CommonLigaturesState(); |
| ligatures.discretionary = DiscretionaryLigaturesState(); |
| ligatures.historical = HistoricalLigaturesState(); |
| ligatures.contextual = ContextualLigaturesState(); |
| |
| return ligatures; |
| } |
| |
| void FontDescription::SetTraits(FontTraits traits) { |
| SetStyle(traits.Style()); |
| SetWeight(traits.Weight()); |
| SetStretch(traits.Stretch()); |
| } |
| |
| void FontDescription::SetVariantCaps(FontVariantCaps variant_caps) { |
| fields_.variant_caps_ = variant_caps; |
| |
| UpdateTypesettingFeatures(); |
| } |
| |
| 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. |
| float computed_or_adjusted_size = |
| HasSizeAdjust() ? AdjustedSize() : ComputedSize(); |
| return floorf(computed_or_adjusted_size * |
| FontCacheKey::PrecisionMultiplier()) / |
| FontCacheKey::PrecisionMultiplier(); |
| } |
| |
| FontCacheKey FontDescription::CacheKey( |
| const FontFaceCreationParams& creation_params, |
| FontTraits desired_traits) const { |
| FontTraits font_traits = |
| desired_traits.Bitfield() ? desired_traits : Traits(); |
| |
| unsigned options = |
| 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 |
| |
| return FontCacheKey(creation_params, EffectiveFontSize(), |
| options | font_traits.Bitfield() << 8, |
| variation_settings_); |
| } |
| |
| void FontDescription::SetDefaultTypesettingFeatures( |
| TypesettingFeatures typesetting_features) { |
| default_typesetting_features_ = typesetting_features; |
| } |
| |
| TypesettingFeatures FontDescription::DefaultTypesettingFeatures() { |
| return default_typesetting_features_; |
| } |
| |
| void FontDescription::UpdateTypesettingFeatures() { |
| fields_.typesetting_features_ = default_typesetting_features_; |
| |
| 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 (http://dev.w3.org/csswg/css-text-3/#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_ == 0) { |
| 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; |
| } |
| |
| static inline void AddToHash(unsigned& hash, unsigned key) { |
| hash = ((hash << 5) + hash) + key; // Djb2 |
| } |
| |
| static inline void AddFloatToHash(unsigned& hash, float value) { |
| AddToHash(hash, StringHasher::HashMemory(&value, sizeof(value))); |
| } |
| |
| unsigned FontDescription::StyleHashWithoutFamilyList() const { |
| unsigned hash = 0; |
| StringHasher string_hasher; |
| const FontFeatureSettings* settings = FeatureSettings(); |
| if (settings) { |
| unsigned num_features = settings->size(); |
| for (unsigned i = 0; i < num_features; ++i) { |
| const AtomicString& tag = settings->at(i).Tag(); |
| for (unsigned j = 0; j < tag.length(); j++) |
| string_hasher.AddCharacter(tag[j]); |
| AddToHash(hash, settings->at(i).Value()); |
| } |
| } |
| |
| if (VariationSettings()) |
| AddToHash(hash, VariationSettings()->GetHash()); |
| |
| if (locale_) { |
| const AtomicString& locale = locale_->LocaleString(); |
| for (unsigned i = 0; i < locale.length(); i++) |
| string_hasher.AddCharacter(locale[i]); |
| } |
| AddToHash(hash, string_hasher.GetHash()); |
| |
| AddFloatToHash(hash, specified_size_); |
| AddFloatToHash(hash, computed_size_); |
| AddFloatToHash(hash, adjusted_size_); |
| AddFloatToHash(hash, size_adjust_); |
| AddFloatToHash(hash, letter_spacing_); |
| AddFloatToHash(hash, word_spacing_); |
| AddToHash(hash, fields_as_unsigned_.parts[0]); |
| AddToHash(hash, fields_as_unsigned_.parts[1]); |
| |
| return hash; |
| } |
| |
| SkFontStyle FontDescription::SkiaFontStyle() const { |
| int width = static_cast<int>(Stretch()); |
| SkFontStyle::Slant slant = SkFontStyle::kUpright_Slant; |
| switch (Style()) { |
| case kFontStyleNormal: |
| slant = SkFontStyle::kUpright_Slant; |
| break; |
| case kFontStyleItalic: |
| slant = SkFontStyle::kItalic_Slant; |
| break; |
| case kFontStyleOblique: |
| slant = SkFontStyle::kOblique_Slant; |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| return SkFontStyle(NumericFontWeight(Weight()), width, slant); |
| static_assert( |
| static_cast<int>(kFontStretchUltraCondensed) == |
| static_cast<int>(SkFontStyle::kUltraCondensed_Width), |
| "FontStretchUltraCondensed should map to kUltraCondensed_Width"); |
| static_assert(static_cast<int>(kFontStretchNormal) == |
| static_cast<int>(SkFontStyle::kNormal_Width), |
| "FontStretchNormal should map to kNormal_Width"); |
| static_assert(static_cast<int>(kFontStretchUltraExpanded) == |
| static_cast<int>(SkFontStyle::kUltraExpanded_Width), |
| "FontStretchUltraExpanded should map to kUltraExpanded_Width"); |
| } |
| |
| } // namespace blink |