1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
|
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
package org.apache.fop.complexscripts.fonts;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
// CSOFF: NoWhitespaceAfterCheck
// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
/**
* <p>Base class implementation of glyph mapping table. This base
* class maps glyph indices to arbitrary integers (mappping indices), and
* is used to implement both glyph coverage and glyph class maps.</p>
*
* <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p>
*/
public class GlyphMappingTable {
/** empty mapping table */
public static final int GLYPH_MAPPING_TYPE_EMPTY = 0;
/** mapped mapping table */
public static final int GLYPH_MAPPING_TYPE_MAPPED = 1;
/** range based mapping table */
public static final int GLYPH_MAPPING_TYPE_RANGE = 2;
/**
* Obtain mapping type.
* @return mapping format type
*/
public int getType() {
return -1;
}
/**
* Obtain mapping entries.
* @return list of mapping entries
*/
public List getEntries() {
return null;
}
/**
* Obtain size of mapping table, i.e., ciMax + 1, where ciMax is the maximum
* mapping index.
* @return size of mapping table
*/
public int getMappingSize() {
return 0;
}
/**
* Map glyph identifier (code) to coverge index. Returns -1 if glyph identifier is not in the domain of
* the mapping table.
* @param gid glyph identifier (code)
* @return non-negative glyph mapping index or -1 if glyph identifiers is not mapped by table
*/
public int getMappedIndex(int gid) {
return -1;
}
/** empty mapping table base class */
protected static class EmptyMappingTable extends GlyphMappingTable {
/**
* Construct empty mapping table.
*/
public EmptyMappingTable() {
this ((List) null);
}
/**
* Construct empty mapping table with entries (ignored).
* @param entries list of entries (ignored)
*/
public EmptyMappingTable(List entries) {
}
/** {@inheritDoc} */
public int getType() {
return GLYPH_MAPPING_TYPE_EMPTY;
}
/** {@inheritDoc} */
public List getEntries() {
return new java.util.ArrayList();
}
/** {@inheritDoc} */
public int getMappingSize() {
return 0;
}
/** {@inheritDoc} */
public int getMappedIndex(int gid) {
return -1;
}
}
/** mapped mapping table base class */
protected static class MappedMappingTable extends GlyphMappingTable {
/**
* Construct mapped mapping table.
*/
public MappedMappingTable() {
}
/** {@inheritDoc} */
public int getType() {
return GLYPH_MAPPING_TYPE_MAPPED;
}
}
/** range mapping table base class */
protected abstract static class RangeMappingTable extends GlyphMappingTable {
private int[] sa = null; // array of range (inclusive) starts
private int[] ea = null; // array of range (inclusive) ends
private int[] ma = null; // array of range mapped values
private int miMax = -1;
/**
* Construct range mapping table.
* @param entries of mapping ranges
*/
public RangeMappingTable(List entries) {
populate(entries);
}
/** {@inheritDoc} */
public int getType() {
return GLYPH_MAPPING_TYPE_RANGE;
}
/** {@inheritDoc} */
public List getEntries() {
List entries = new java.util.ArrayList();
if (sa != null) {
for (int i = 0, n = sa.length; i < n; i++) {
entries.add(new MappingRange(sa [ i ], ea [ i ], ma [ i ]));
}
}
return entries;
}
/** {@inheritDoc} */
public int getMappingSize() {
return miMax + 1;
}
/** {@inheritDoc} */
public int getMappedIndex(int gid) {
int i;
int mi;
if ((i = Arrays.binarySearch(sa, gid)) >= 0) {
mi = getMappedIndex(gid, sa [ i ], ma [ i ]); // matches start of (some) range
} else if ((i = - (i + 1)) == 0) {
mi = -1; // precedes first range
} else if (gid > ea [ --i ]) {
mi = -1; // follows preceding (or last) range
} else {
mi = getMappedIndex(gid, sa [ i ], ma [ i ]); // intersects (some) range
}
return mi;
}
/**
* Map glyph identifier (code) to coverge index. Returns -1 if glyph identifier is not in the domain of
* the mapping table.
* @param gid glyph identifier (code)
* @param s start of range
* @param m mapping value
* @return non-negative glyph mapping index or -1 if glyph identifiers is not mapped by table
*/
public abstract int getMappedIndex(int gid, int s, int m);
private void populate(List entries) {
int i = 0;
int n = entries.size();
int gidMax = -1;
int miMax = -1;
int[] sa = new int [ n ];
int[] ea = new int [ n ];
int[] ma = new int [ n ];
for (Iterator it = entries.iterator(); it.hasNext();) {
Object o = it.next();
if (o instanceof MappingRange) {
MappingRange r = (MappingRange) o;
int gs = r.getStart();
int ge = r.getEnd();
int mi = r.getIndex();
if ((gs < 0) || (gs > 65535)) {
throw new AdvancedTypographicTableFormatException("illegal glyph range: [" + gs + "," + ge + "]: bad start index");
} else if ((ge < 0) || (ge > 65535)) {
throw new AdvancedTypographicTableFormatException("illegal glyph range: [" + gs + "," + ge + "]: bad end index");
} else if (gs > ge) {
throw new AdvancedTypographicTableFormatException("illegal glyph range: [" + gs + "," + ge + "]: start index exceeds end index");
} else if (gs < gidMax) {
throw new AdvancedTypographicTableFormatException("out of order glyph range: [" + gs + "," + ge + "]");
} else if (mi < 0) {
throw new AdvancedTypographicTableFormatException("illegal mapping index: " + mi);
} else {
int miLast;
sa [ i ] = gs;
ea [ i ] = gidMax = ge;
ma [ i ] = mi;
if ((miLast = mi + (ge - gs)) > miMax) {
miMax = miLast;
}
i++;
}
} else {
throw new AdvancedTypographicTableFormatException("illegal mapping entry, must be Integer: " + o);
}
}
assert i == n;
assert this.sa == null;
assert this.ea == null;
assert this.ma == null;
this.sa = sa;
this.ea = ea;
this.ma = ma;
this.miMax = miMax;
}
/** {@inheritDoc} */
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append('{');
for (int i = 0, n = sa.length; i < n; i++) {
if (i > 0) {
sb.append(',');
}
sb.append('[');
sb.append(Integer.toString(sa [ i ]));
sb.append(Integer.toString(ea [ i ]));
sb.append("]:");
sb.append(Integer.toString(ma [ i ]));
}
sb.append('}');
return sb.toString();
}
}
/**
* The <code>MappingRange</code> class encapsulates a glyph [start,end] range and
* a mapping index.
*/
public static class MappingRange {
private final int gidStart; // first glyph in range (inclusive)
private final int gidEnd; // last glyph in range (inclusive)
private final int index; // mapping index;
/**
* Instantiate a mapping range.
*/
public MappingRange() {
this (0, 0, 0);
}
/**
* Instantiate a specific mapping range.
* @param gidStart start of range
* @param gidEnd end of range
* @param index mapping index
*/
public MappingRange(int gidStart, int gidEnd, int index) {
if ((gidStart < 0) || (gidEnd < 0) || (index < 0)) {
throw new AdvancedTypographicTableFormatException();
} else if (gidStart > gidEnd) {
throw new AdvancedTypographicTableFormatException();
} else {
this.gidStart = gidStart;
this.gidEnd = gidEnd;
this.index = index;
}
}
/** @return start of range */
public int getStart() {
return gidStart;
}
/** @return end of range */
public int getEnd() {
return gidEnd;
}
/** @return mapping index */
public int getIndex() {
return index;
}
/** @return interval as a pair of integers */
public int[] getInterval() {
return new int[] { gidStart, gidEnd };
}
/**
* Obtain interval, filled into first two elements of specified array, or returning new array.
* @param interval an array of length two or greater or null
* @return interval as a pair of integers, filled into specified array
*/
public int[] getInterval(int[] interval) {
if ((interval == null) || (interval.length != 2)) {
throw new IllegalArgumentException();
} else {
interval[0] = gidStart;
interval[1] = gidEnd;
}
return interval;
}
/** @return length of interval */
public int getLength() {
return gidStart - gidEnd;
}
}
}
|