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