xref: /aosp_15_r20/frameworks/minikin/libs/minikin/FontFeatureUtils.cpp (revision 834a2baab5fdfc28e9a428ee87c7ea8f6a06a53d)
1*834a2baaSAndroid Build Coastguard Worker /*
2*834a2baaSAndroid Build Coastguard Worker  * Copyright (C) 2021 The Android Open Source Project
3*834a2baaSAndroid Build Coastguard Worker  *
4*834a2baaSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*834a2baaSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*834a2baaSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*834a2baaSAndroid Build Coastguard Worker  *
8*834a2baaSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*834a2baaSAndroid Build Coastguard Worker  *
10*834a2baaSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*834a2baaSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*834a2baaSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*834a2baaSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*834a2baaSAndroid Build Coastguard Worker  * limitations under the License.
15*834a2baaSAndroid Build Coastguard Worker  */
16*834a2baaSAndroid Build Coastguard Worker 
17*834a2baaSAndroid Build Coastguard Worker #include "StringPiece.h"
18*834a2baaSAndroid Build Coastguard Worker #include "minikin/FontFeature.h"
19*834a2baaSAndroid Build Coastguard Worker #include "minikin/MinikinPaint.h"
20*834a2baaSAndroid Build Coastguard Worker 
21*834a2baaSAndroid Build Coastguard Worker namespace minikin {
22*834a2baaSAndroid Build Coastguard Worker 
parse(std::string_view fontFeatureSettings)23*834a2baaSAndroid Build Coastguard Worker std::vector<FontFeature> FontFeature::parse(std::string_view fontFeatureSettings) {
24*834a2baaSAndroid Build Coastguard Worker     std::vector<FontFeature> features;
25*834a2baaSAndroid Build Coastguard Worker 
26*834a2baaSAndroid Build Coastguard Worker     SplitIterator it(StringPiece(fontFeatureSettings), ',');
27*834a2baaSAndroid Build Coastguard Worker     while (it.hasNext()) {
28*834a2baaSAndroid Build Coastguard Worker         StringPiece featureStr = it.next();
29*834a2baaSAndroid Build Coastguard Worker         static hb_feature_t feature;
30*834a2baaSAndroid Build Coastguard Worker         // We do not allow setting features on ranges. As such, reject any setting that has
31*834a2baaSAndroid Build Coastguard Worker         // non-universal range.
32*834a2baaSAndroid Build Coastguard Worker         if (hb_feature_from_string(featureStr.data(), featureStr.size(), &feature) &&
33*834a2baaSAndroid Build Coastguard Worker             feature.start == 0 && feature.end == (unsigned int)-1) {
34*834a2baaSAndroid Build Coastguard Worker             features.push_back({feature.tag, feature.value});
35*834a2baaSAndroid Build Coastguard Worker         }
36*834a2baaSAndroid Build Coastguard Worker     }
37*834a2baaSAndroid Build Coastguard Worker     return features;
38*834a2baaSAndroid Build Coastguard Worker }
39*834a2baaSAndroid Build Coastguard Worker 
cleanAndAddDefaultFontFeatures(const MinikinPaint & paint)40*834a2baaSAndroid Build Coastguard Worker std::vector<hb_feature_t> cleanAndAddDefaultFontFeatures(const MinikinPaint& paint) {
41*834a2baaSAndroid Build Coastguard Worker     std::vector<hb_feature_t> features;
42*834a2baaSAndroid Build Coastguard Worker     // Disable default-on non-required ligature features if letter-spacing
43*834a2baaSAndroid Build Coastguard Worker     // See http://dev.w3.org/csswg/css-text-3/#letter-spacing-property
44*834a2baaSAndroid Build Coastguard Worker     // "When the effective spacing between two characters is not zero (due to
45*834a2baaSAndroid Build Coastguard Worker     // either justification or a non-zero value of letter-spacing), user agents
46*834a2baaSAndroid Build Coastguard Worker     // should not apply optional ligatures."
47*834a2baaSAndroid Build Coastguard Worker     if (fabs(paint.letterSpacing) > 0.03) {
48*834a2baaSAndroid Build Coastguard Worker         static constexpr hb_feature_t no_liga = {HB_TAG('l', 'i', 'g', 'a'), 0, 0, ~0u};
49*834a2baaSAndroid Build Coastguard Worker         static constexpr hb_feature_t no_clig = {HB_TAG('c', 'l', 'i', 'g'), 0, 0, ~0u};
50*834a2baaSAndroid Build Coastguard Worker         features.push_back(no_liga);
51*834a2baaSAndroid Build Coastguard Worker         features.push_back(no_clig);
52*834a2baaSAndroid Build Coastguard Worker     }
53*834a2baaSAndroid Build Coastguard Worker 
54*834a2baaSAndroid Build Coastguard Worker     bool default_enable_chws = true;
55*834a2baaSAndroid Build Coastguard Worker 
56*834a2baaSAndroid Build Coastguard Worker     static constexpr hb_tag_t chws_tag = HB_TAG('c', 'h', 'w', 's');
57*834a2baaSAndroid Build Coastguard Worker     static constexpr hb_tag_t halt_tag = HB_TAG('h', 'a', 'l', 't');
58*834a2baaSAndroid Build Coastguard Worker     static constexpr hb_tag_t palt_tag = HB_TAG('p', 'a', 'l', 't');
59*834a2baaSAndroid Build Coastguard Worker 
60*834a2baaSAndroid Build Coastguard Worker     for (const FontFeature& feature : paint.fontFeatureSettings) {
61*834a2baaSAndroid Build Coastguard Worker         // OpenType requires disabling default `chws` feature if glyph-width features.
62*834a2baaSAndroid Build Coastguard Worker         // https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#tag-chws
63*834a2baaSAndroid Build Coastguard Worker         // Here, we follow Chrome's impl: not enabling default `chws` feature if `palt` or
64*834a2baaSAndroid Build Coastguard Worker         // `halt` is enabled.
65*834a2baaSAndroid Build Coastguard Worker         // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/fonts/shaping/font_features.cc;drc=77a9a09de0688ca449f5333a305ceaf3f36b6daf;l=215
66*834a2baaSAndroid Build Coastguard Worker         if (feature.tag == chws_tag ||
67*834a2baaSAndroid Build Coastguard Worker             (feature.value && (feature.tag == halt_tag || feature.tag == palt_tag))) {
68*834a2baaSAndroid Build Coastguard Worker             default_enable_chws = false;
69*834a2baaSAndroid Build Coastguard Worker         }
70*834a2baaSAndroid Build Coastguard Worker         features.push_back({feature.tag, feature.value, 0, ~0u});
71*834a2baaSAndroid Build Coastguard Worker     }
72*834a2baaSAndroid Build Coastguard Worker 
73*834a2baaSAndroid Build Coastguard Worker     if (default_enable_chws) {
74*834a2baaSAndroid Build Coastguard Worker         static constexpr hb_feature_t chws = {chws_tag, 1, 0, ~0u};
75*834a2baaSAndroid Build Coastguard Worker         features.push_back(chws);
76*834a2baaSAndroid Build Coastguard Worker     }
77*834a2baaSAndroid Build Coastguard Worker 
78*834a2baaSAndroid Build Coastguard Worker     return features;
79*834a2baaSAndroid Build Coastguard Worker }
80*834a2baaSAndroid Build Coastguard Worker 
81*834a2baaSAndroid Build Coastguard Worker }  // namespace minikin
82