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