| /* |
| * This file is part of the internal font implementation. |
| * |
| * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. |
| * Copyright (c) 2010 Google Inc. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #import "third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.h" |
| |
| #import <AppKit/AppKit.h> |
| #import <AvailabilityMacros.h> |
| |
| #include "base/mac/bridging.h" |
| #import "base/mac/foundation_util.h" |
| #include "third_party/blink/renderer/platform/fonts/font.h" |
| #include "third_party/blink/renderer/platform/fonts/font_platform_data.h" |
| #include "third_party/blink/renderer/platform/fonts/mac/core_text_font_format_support.h" |
| #include "third_party/blink/renderer/platform/fonts/opentype/font_settings.h" |
| #include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h" |
| #include "third_party/blink/renderer/platform/web_test_support.h" |
| #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" |
| #include "third_party/blink/renderer/platform/wtf/vector.h" |
| #include "third_party/skia/include/core/SkFont.h" |
| #include "third_party/skia/include/core/SkStream.h" |
| #include "third_party/skia/include/core/SkTypeface.h" |
| #include "third_party/skia/include/core/SkTypes.h" |
| #import "third_party/skia/include/ports/SkTypeface_mac.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace { |
| constexpr SkFourByteTag kOpszTag = SkSetFourByteTag('o', 'p', 's', 'z'); |
| } |
| |
| namespace blink { |
| |
| bool VariableAxisChangeEffective(SkTypeface* typeface, |
| SkFourByteTag axis, |
| float new_value) { |
| // First clamp new value to within range of min and max of variable axis. |
| int num_axes = typeface->getVariationDesignParameters(nullptr, 0); |
| if (num_axes <= 0) |
| return false; |
| |
| Vector<SkFontParameters::Variation::Axis> axes_parameters(num_axes); |
| int returned_axes = |
| typeface->getVariationDesignParameters(axes_parameters.data(), num_axes); |
| DCHECK_EQ(num_axes, returned_axes); |
| DCHECK_GE(num_axes, 0); |
| |
| float clamped_new_value = new_value; |
| for (auto& axis_parameters : axes_parameters) { |
| if (axis_parameters.tag == axis) { |
| clamped_new_value = std::min(new_value, axis_parameters.max); |
| clamped_new_value = std::max(clamped_new_value, axis_parameters.min); |
| } |
| } |
| |
| int num_coordinates = typeface->getVariationDesignPosition(nullptr, 0); |
| if (num_coordinates <= 0) |
| return true; // Font has axes, but no positions, setting one would have an |
| // effect. |
| |
| // Then compare if clamped value differs from what is set on the font. |
| Vector<SkFontArguments::VariationPosition::Coordinate> coordinates( |
| num_coordinates); |
| int returned_coordinates = |
| typeface->getVariationDesignPosition(coordinates.data(), num_coordinates); |
| |
| if (returned_coordinates != num_coordinates) |
| return false; // Something went wrong in retrieving actual axis positions, |
| // font broken? |
| |
| for (auto& coordinate : coordinates) { |
| if (coordinate.axis == axis) |
| return coordinate.value != clamped_new_value; |
| } |
| return false; |
| } |
| |
| static bool CanLoadInProcess(NSFont* ns_font) { |
| base::ScopedCFTypeRef<CGFontRef> cg_font(CTFontCopyGraphicsFont( |
| base::mac::NSToCFPtrCast(ns_font), /*attributes=*/nullptr)); |
| NSString* font_name = |
| base::mac::CFToNSOwnershipCast(CGFontCopyPostScriptName(cg_font)); |
| return ![font_name isEqualToString:@"LastResort"]; |
| } |
| |
| std::unique_ptr<FontPlatformData> FontPlatformDataFromNSFont( |
| NSFont* ns_font, |
| float size, |
| float specified_size, |
| bool synthetic_bold, |
| bool synthetic_italic, |
| TextRenderingMode text_rendering, |
| ResolvedFontFeatures resolved_font_features, |
| FontOrientation orientation, |
| OpticalSizing optical_sizing, |
| FontVariationSettings* variation_settings) { |
| DCHECK(ns_font); |
| |
| // fontd automatically issues a sandbox extension to permit reading |
| // activated fonts that would otherwise be restricted by the sandbox. |
| DCHECK(CanLoadInProcess(ns_font)); |
| |
| sk_sp<SkTypeface> typeface = |
| SkMakeTypefaceFromCTFont(base::mac::NSToCFPtrCast(ns_font)); |
| |
| auto make_typeface_fontplatformdata = [&typeface, &size, &synthetic_bold, |
| &synthetic_italic, &text_rendering, |
| resolved_font_features, |
| &orientation]() { |
| return std::make_unique<FontPlatformData>( |
| std::move(typeface), std::string(), size, synthetic_bold, |
| synthetic_italic, text_rendering, resolved_font_features, orientation); |
| }; |
| |
| wtf_size_t valid_configured_axes = |
| variation_settings && variation_settings->size() < UINT16_MAX |
| ? variation_settings->size() |
| : 0; |
| |
| // No variable font requested, return static font. |
| if (!valid_configured_axes && optical_sizing == kNoneOpticalSizing) |
| return make_typeface_fontplatformdata(); |
| |
| if (!typeface) |
| return nullptr; |
| |
| int existing_axes = typeface->getVariationDesignPosition(nullptr, 0); |
| // Don't apply variation parameters if the font does not have axes or we |
| // fail to retrieve the existing ones. |
| if (existing_axes <= 0) |
| return make_typeface_fontplatformdata(); |
| |
| Vector<SkFontArguments::VariationPosition::Coordinate> coordinates_to_set; |
| coordinates_to_set.resize(existing_axes); |
| |
| if (typeface->getVariationDesignPosition(coordinates_to_set.data(), |
| existing_axes) != existing_axes) { |
| return make_typeface_fontplatformdata(); |
| } |
| |
| // Iterate over the font's axes and find a missing tag from variation |
| // settings, special case 'opsz', track the number of axes reconfigured. |
| bool axes_reconfigured = false; |
| for (auto& coordinate : coordinates_to_set) { |
| // Set 'opsz' to specified size but allow having it overridden by |
| // font-variation-settings in case it has 'opsz'. Do not use font size here, |
| // but specified size in order to account for zoom. |
| if (coordinate.axis == kOpszTag && optical_sizing == kAutoOpticalSizing) { |
| if (VariableAxisChangeEffective(typeface.get(), coordinate.axis, |
| specified_size)) { |
| coordinate.value = SkFloatToScalar(specified_size); |
| axes_reconfigured = true; |
| } |
| } |
| FontVariationAxis found_variation_setting(0, 0); |
| if (variation_settings && variation_settings->FindPair( |
| coordinate.axis, &found_variation_setting)) { |
| if (VariableAxisChangeEffective(typeface.get(), coordinate.axis, |
| found_variation_setting.Value())) { |
| coordinate.value = found_variation_setting.Value(); |
| axes_reconfigured = true; |
| } |
| } |
| } |
| |
| if (!axes_reconfigured) { |
| // No variable axes touched, return the previous typeface. |
| return make_typeface_fontplatformdata(); |
| } |
| |
| SkFontArguments::VariationPosition variation_design_position{ |
| coordinates_to_set.data(), static_cast<int>(coordinates_to_set.size())}; |
| |
| sk_sp<SkTypeface> cloned_typeface(typeface->makeClone( |
| SkFontArguments().setVariationDesignPosition(variation_design_position))); |
| |
| if (!cloned_typeface) { |
| // Applying variation parameters failed, return original typeface. |
| return make_typeface_fontplatformdata(); |
| } |
| typeface = cloned_typeface; |
| return make_typeface_fontplatformdata(); |
| } |
| |
| SkFont FontPlatformData::CreateSkFont( |
| const FontDescription* font_description) const { |
| bool should_smooth_fonts = true; |
| bool should_antialias = true; |
| bool should_subpixel_position = true; |
| |
| if (font_description) { |
| switch (font_description->FontSmoothing()) { |
| case kAntialiased: |
| should_smooth_fonts = false; |
| break; |
| case kSubpixelAntialiased: |
| break; |
| case kNoSmoothing: |
| should_antialias = false; |
| should_smooth_fonts = false; |
| break; |
| case kAutoSmoothing: |
| // For the AutoSmooth case, don't do anything! Keep the default |
| // settings. |
| break; |
| } |
| } |
| |
| if (WebTestSupport::IsRunningWebTest()) { |
| should_smooth_fonts = false; |
| should_antialias = |
| should_antialias && WebTestSupport::IsFontAntialiasingEnabledForTest(); |
| should_subpixel_position = |
| WebTestSupport::IsTextSubpixelPositioningAllowedForTest(); |
| } |
| |
| SkFont skfont; |
| if (should_antialias && should_smooth_fonts) { |
| skfont.setEdging(SkFont::Edging::kSubpixelAntiAlias); |
| } else if (should_antialias) { |
| skfont.setEdging(SkFont::Edging::kAntiAlias); |
| } else { |
| skfont.setEdging(SkFont::Edging::kAlias); |
| } |
| skfont.setEmbeddedBitmaps(false); |
| const float ts = text_size_ >= 0 ? text_size_ : 12; |
| skfont.setSize(SkFloatToScalar(ts)); |
| skfont.setTypeface(typeface_); |
| skfont.setEmbolden(synthetic_bold_); |
| skfont.setSkewX(synthetic_italic_ ? -SK_Scalar1 / 4 : 0); |
| skfont.setSubpixel(should_subpixel_position); |
| |
| // CoreText always provides linear metrics if it can, so the linear metrics |
| // flag setting doesn't affect typefaces backed by CoreText. However, it |
| // does affect FreeType backed typefaces, so set the flag for consistency. |
| skfont.setLinearMetrics(should_subpixel_position); |
| |
| // When rendering using CoreGraphics, disable hinting when |
| // webkit-font-smoothing:antialiased or text-rendering:geometricPrecision is |
| // used. See crbug.com/152304 |
| if (font_description && |
| (font_description->FontSmoothing() == kAntialiased || |
| font_description->TextRendering() == kGeometricPrecision)) |
| skfont.setHinting(SkFontHinting::kNone); |
| return skfont; |
| } |
| |
| } // namespace blink |