2 * Copyright © 2009,2010 Red Hat, Inc.
3 * Copyright © 2010,2011,2013 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
31 #ifndef HB_NO_AAT_SHAPE
33 #include "hb-aat-map.hh"
35 #include "hb-aat-layout.hh"
36 #include "hb-aat-layout-feat-table.hh"
39 void hb_aat_map_builder_t::add_feature (const hb_feature_t &feature)
41 if (!face->table.feat->has_data ()) return;
43 if (feature.tag == HB_TAG ('a','a','l','t'))
45 if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES))
47 feature_range_t *range = features.push();
48 range->start = feature.start;
49 range->end = feature.end;
50 range->info.type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
51 range->info.setting = (hb_aat_layout_feature_selector_t) feature.value;
52 range->info.seq = features.length;
53 range->info.is_exclusive = true;
57 const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (feature.tag);
60 const AAT::FeatureName* feature_name = &face->table.feat->get_feature (mapping->aatFeatureType);
61 if (!feature_name->has_data ())
63 /* Special case: Chain::compile_flags will fall back to the deprecated version of
64 * small-caps if necessary, so we need to check for that possibility.
65 * https://github.com/harfbuzz/harfbuzz/issues/2307 */
66 if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE &&
67 mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS)
69 feature_name = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
70 if (!feature_name->has_data ()) return;
75 feature_range_t *range = features.push();
76 range->start = feature.start;
77 range->end = feature.end;
78 range->info.type = mapping->aatFeatureType;
79 range->info.setting = feature.value ? mapping->selectorToEnable : mapping->selectorToDisable;
80 range->info.seq = features.length;
81 range->info.is_exclusive = feature_name->is_exclusive ();
85 hb_aat_map_builder_t::compile (hb_aat_map_t &m)
87 /* Compute active features per range, and compile each. */
89 /* Sort features by start/end events. */
90 hb_vector_t<feature_event_t> feature_events;
91 for (unsigned int i = 0; i < features.length; i++)
93 auto &feature = features[i];
95 if (features[i].start == features[i].end)
98 feature_event_t *event;
100 event = feature_events.push ();
101 event->index = features[i].start;
103 event->feature = feature.info;
105 event = feature_events.push ();
106 event->index = features[i].end;
107 event->start = false;
108 event->feature = feature.info;
110 feature_events.qsort ();
111 /* Add a strategic final event. */
113 feature_info_t feature;
114 feature.seq = features.length + 1;
116 feature_event_t *event = feature_events.push ();
117 event->index = -1; /* This value does magic. */
118 event->start = false;
119 event->feature = feature;
122 /* Scan events and save features for each range. */
123 hb_sorted_vector_t<feature_info_t> active_features;
124 unsigned int last_index = 0;
125 for (unsigned int i = 0; i < feature_events.length; i++)
127 feature_event_t *event = &feature_events[i];
129 if (event->index != last_index)
131 /* Save a snapshot of active features and the range. */
133 /* Sort features and merge duplicates */
134 current_features = active_features;
135 range_first = last_index;
136 range_last = event->index - 1;
137 if (current_features.length)
139 current_features.qsort ();
141 for (unsigned int i = 1; i < current_features.length; i++)
142 if (current_features[i].type != current_features[j].type ||
143 /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
144 * respectively, so we mask out the low-order bit when checking for "duplicates"
145 * (selectors referring to the same feature setting) here. */
146 (!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1))))
147 current_features[++j] = current_features[i];
148 current_features.shrink (j + 1);
151 hb_aat_layout_compile_map (this, &m);
153 last_index = event->index;
158 active_features.push (event->feature);
160 feature_info_t *feature = active_features.lsearch (event->feature);
162 active_features.remove_ordered (feature - active_features.arrayZ);
166 for (auto &chain_flags : m.chain_flags)
167 // With our above setup this value is one less than desired; adjust it.
168 chain_flags.tail().cluster_last = HB_FEATURE_GLOBAL_END;