1 /* glyphfilter.{cc,hh} -- define subsets of characters
2 *
3 * Copyright (c) 2004-2012 Eddie Kohler
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version. This program is distributed in the hope that it will be
9 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
12 */
13
14 #ifdef HAVE_CONFIG_H
15 # include <config.h>
16 #endif
17 #ifdef WIN32
18 # define _USE_MATH_DEFINES
19 #endif
20 #include "glyphfilter.hh"
21 #include <lcdf/error.hh>
22 #include <lcdf/straccum.hh>
23 #include <ctype.h>
24 #include <algorithm>
25 #include "uniprop.hh"
26 #include "util.hh"
27
28 bool
allow(Efont::OpenType::Glyph glyph,const Vector<PermString> & glyph_names,uint32_t unicode,int ptype) const29 GlyphFilter::allow(Efont::OpenType::Glyph glyph, const Vector<PermString>& glyph_names, uint32_t unicode, int ptype) const
30 {
31 // out-of-range glyphs never match
32 if (glyph < 0 || glyph >= glyph_names.size())
33 return false;
34
35 String glyph_name = glyph_names[glyph];
36 int uniprop = -1;
37 bool any_includes = false;
38 bool included = false;
39
40 // loop over patterns
41 for (const Pattern* p = _patterns.begin(); p < _patterns.end(); p++) {
42 // check pattern type
43 if ((p->type & ~T_TYPEMASK) != ptype)
44 continue;
45 // check include/exclude
46 if ((p->type & T_EXCLUDE) == 0) {
47 if (included)
48 continue;
49 any_includes = true;
50 }
51 // check if there's a match
52 bool match;
53 if (p->data == D_NAME)
54 match = glob_match(glyph_name, p->pattern);
55 else if (p->data == D_UNIPROP) {
56 if (uniprop < 0)
57 uniprop = UnicodeProperty::property(unicode);
58 match = ((uniprop & p->u.uniprop.mask) == p->u.uniprop.value);
59 } else
60 match = (unicode >= p->u.unirange.low && unicode <= p->u.unirange.high);
61 // act if match
62 if (match == ((p->type & T_NEGATE) == 0)) {
63 if ((p->type & T_EXCLUDE) == 0)
64 included = true;
65 else
66 return false;
67 }
68 }
69
70 return !any_includes || included;
71 }
72
Pattern(uint16_t ptype)73 GlyphFilter::Pattern::Pattern(uint16_t ptype)
74 : type(ptype), data(D_NAME)
75 {
76 // make sure that even unused data has a known value, to simplify
77 // operator==
78 u.unirange.low = u.unirange.high = 0;
79 }
80
81 int
compare(const GlyphFilter::Pattern & a,const GlyphFilter::Pattern & b)82 GlyphFilter::Pattern::compare(const GlyphFilter::Pattern& a, const GlyphFilter::Pattern& b)
83 {
84 int cmp = a.type - b.type;
85 if (cmp == 0)
86 cmp = a.data - b.data;
87 if (cmp == 0)
88 cmp = (int) (a.u.unirange.low - b.u.unirange.low);
89 if (cmp == 0)
90 cmp = (int) (a.u.unirange.high - b.u.unirange.high);
91 if (cmp == 0)
92 cmp = String::compare(a.pattern, b.pattern);
93 return cmp;
94 }
95
96 void
add_pattern(const String & pattern,int ptype,ErrorHandler * errh)97 GlyphFilter::add_pattern(const String& pattern, int ptype, ErrorHandler* errh)
98 {
99 _sorted = false;
100
101 const char* begin = pattern.begin();
102 const char* end = pattern.end();
103 while (begin < end && isspace((unsigned char) *begin))
104 begin++;
105 if (begin >= end)
106 errh->error("missing pattern");
107
108 while (begin < end) {
109 const char* word = begin;
110 while (word < end && !isspace((unsigned char) *word))
111 word++;
112 bool negated = false;
113 if (begin < word && begin[0] == '!')
114 negated = true, begin++;
115
116 // actually parse clause
117 Pattern p(ptype + (negated ? T_NEGATE : 0));
118
119 // unicode property
120 if (begin + 3 <= word && begin[0] == '<' && word[-1] == '>') {
121 p.data = D_UNIPROP;
122 if (UnicodeProperty::parse_property(pattern.substring(begin + 1, word - 1), p.u.uniprop.value, p.u.uniprop.mask))
123 _patterns.push_back(p);
124 else if (errh)
125 errh->error("unknown Unicode property %<%s%>", pattern.c_str());
126 goto next_clause;
127 }
128
129 // unicode values
130 {
131 const char* dash = std::find(begin, word, '-');
132 if (parse_unicode_number(begin, dash, 2, p.u.unirange.low)) {
133 if (dash == word)
134 p.u.unirange.high = p.u.unirange.low;
135 else if (dash == word - 1)
136 p.u.unirange.high = 0xFFFFFFFFU;
137 else if (parse_unicode_number(dash + 1, word, (begin[0] == 'U' ? 1 : 0), p.u.unirange.high))
138 /* do nothing */;
139 else
140 goto name_pattern; // assume it's a name
141 p.data = D_UNIRANGE;
142 _patterns.push_back(p);
143 goto next_clause;
144 }
145 }
146
147 // otherwise must be name pattern
148 name_pattern:
149 p.data = D_NAME;
150 p.pattern = pattern.substring(begin, word);
151 _patterns.push_back(p);
152
153 // move to next clause
154 next_clause:
155 for (begin = word; begin < end && isspace((unsigned char) *begin); begin++)
156 /* nada */;
157 }
158 }
159
160 void
add_substitution_filter(const String & s,bool is_exclude,ErrorHandler * errh)161 GlyphFilter::add_substitution_filter(const String& s, bool is_exclude, ErrorHandler* errh)
162 {
163 add_pattern(s, is_exclude ? T_SRC + T_EXCLUDE : T_SRC, errh);
164 }
165
166 void
add_alternate_filter(const String & s,bool is_exclude,ErrorHandler * errh)167 GlyphFilter::add_alternate_filter(const String& s, bool is_exclude, ErrorHandler* errh)
168 {
169 add_pattern(s, is_exclude ? T_DST + T_EXCLUDE : T_DST, errh);
170 }
171
172 GlyphFilter&
operator +=(const GlyphFilter & gf)173 GlyphFilter::operator+=(const GlyphFilter& gf)
174 {
175 // be careful about self-addition
176 _patterns.reserve(gf._patterns.size());
177 const Pattern* end = gf._patterns.end();
178 for (const Pattern* p = gf._patterns.begin(); p < end; p++)
179 _patterns.push_back(*p);
180 return *this;
181 }
182
183 GlyphFilter
operator +(const GlyphFilter & a,const GlyphFilter & b)184 operator+(const GlyphFilter& a, const GlyphFilter& b)
185 {
186 if (!b)
187 return a;
188 if (!a)
189 return b;
190 GlyphFilter x(a);
191 x += b;
192 return x;
193 }
194
195 bool
operator ==(const GlyphFilter & a,const GlyphFilter & b)196 operator==(const GlyphFilter& a, const GlyphFilter& b)
197 {
198 if (&a == &b)
199 return true;
200 if (a._patterns.size() != b._patterns.size())
201 return false;
202 const GlyphFilter::Pattern* pa = a._patterns.begin();
203 const GlyphFilter::Pattern* pb = b._patterns.begin();
204 for (; pa < a._patterns.end(); pa++, pb++)
205 if (!(*pa == *pb))
206 return false;
207 return true;
208 }
209
210 void
sort()211 GlyphFilter::sort()
212 {
213 if (!_sorted) {
214 std::sort(_patterns.begin(), _patterns.end());
215 Pattern* true_end = std::unique(_patterns.begin(), _patterns.end());
216 _patterns.erase(true_end, _patterns.end());
217 _sorted = true;
218 }
219 }
220
221 void
unparse(StringAccum & sa) const222 GlyphFilter::unparse(StringAccum& sa) const
223 {
224 for (const Pattern* p = _patterns.begin(); p < _patterns.end(); p++) {
225 sa << (p->type & T_DST ? 'D' : 'S') << (p->type & T_NEGATE ? "!" : "") << (p->type & T_EXCLUDE ? "X" : "");
226 if (p->data == D_NAME)
227 sa << '<' << p->pattern << '>';
228 else if (p->data == D_UNIPROP)
229 sa << "[UNIPROP:" << p->u.uniprop.mask << '=' << p->u.uniprop.value << ']';
230 else
231 sa.snprintf(20, "[U+%02x-U+%02x]", p->u.unirange.low, p->u.unirange.high);
232 sa << ' ';
233 }
234 if (_patterns.size())
235 sa.pop_back();
236 }
237