1/*
2 * Copyright © 2009,2010 Red Hat, Inc.
3 * Copyright © 2010,2011,2013 Google, Inc.
4 *
5 * This is part of HarfBuzz, a text shaping library.
6 *
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.
12 *
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
17 * DAMAGE.
18 *
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.
24 *
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
27 */
28
29#include "hb.hh"
30
31#ifndef HB_NO_AAT_SHAPE
32
33#include "hb-aat-map.hh"
34
35#include "hb-aat-layout.hh"
36#include "hb-aat-layout-feat-table.hh"
37
38
39void hb_aat_map_builder_t::add_feature (const hb_feature_t &feature)
40{
41 if (!face->table.feat->has_data ()) return;
42
43 if (feature.tag == HB_TAG ('a','a','l','t'))
44 {
45 if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES))
46 return;
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;
54 return;
55 }
56
57 const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (feature.tag);
58 if (!mapping) return;
59
60 const AAT::FeatureName* feature_name = &face->table.feat->get_feature (mapping->aatFeatureType);
61 if (!feature_name->has_data ())
62 {
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)
68 {
69 feature_name = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
70 if (!feature_name->has_data ()) return;
71 }
72 else return;
73 }
74
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 ();
82}
83
84void
85hb_aat_map_builder_t::compile (hb_aat_map_t &m)
86{
87 /* Compute active features per range, and compile each. */
88
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++)
92 {
93 auto &feature = features[i];
94
95 if (features[i].start == features[i].end)
96 continue;
97
98 feature_event_t *event;
99
100 event = feature_events.push ();
101 event->index = features[i].start;
102 event->start = true;
103 event->feature = feature.info;
104
105 event = feature_events.push ();
106 event->index = features[i].end;
107 event->start = false;
108 event->feature = feature.info;
109 }
110 feature_events.qsort ();
111 /* Add a strategic final event. */
112 {
113 feature_info_t feature;
114 feature.seq = features.length + 1;
115
116 feature_event_t *event = feature_events.push ();
117 event->index = -1; /* This value does magic. */
118 event->start = false;
119 event->feature = feature;
120 }
121
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++)
126 {
127 feature_event_t *event = &feature_events[i];
128
129 if (event->index != last_index)
130 {
131 /* Save a snapshot of active features and the range. */
132
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)
138 {
139 current_features.qsort ();
140 unsigned int j = 0;
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);
149 }
150
151 hb_aat_layout_compile_map (this, &m);
152
153 last_index = event->index;
154 }
155
156 if (event->start)
157 {
158 active_features.push (event->feature);
159 } else {
160 feature_info_t *feature = active_features.lsearch (event->feature);
161 if (feature)
162 active_features.remove_ordered (feature - active_features.arrayZ);
163 }
164 }
165
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;
169}
170
171
172#endif
173