1
2 package org.freehep.graphicsio.font.truetype;
3
4 import java.awt.Rectangle;
5 import java.awt.geom.AffineTransform;
6 import java.awt.geom.GeneralPath;
7 import java.io.IOException;
8
9
10
11
12
13
14
15 public class TTFGlyfTable extends TTFVersionTable {
16
17
18
19
20
21 private static final boolean READ_GLYPHS = false;
22
23 public abstract class Glyph {
24
25 public int xMin, yMin, xMax, yMax;
26
27 public abstract String getType();
28
29 public abstract GeneralPath getShape();
30
31 public void read() throws IOException {
32 xMin = ttf.readFWord();
33 yMin = ttf.readFWord();
34 xMax = ttf.readFWord();
35 yMax = ttf.readFWord();
36 }
37
38 public Rectangle getBBox() {
39 return new Rectangle(xMin, yMin, xMax - xMin, yMax - yMin);
40 }
41
42 public String toString() {
43 return "[" + getType() + "] (" + xMin + "," + yMin + "):(" + xMax
44 + "," + yMax + ")";
45 }
46
47 public String toDetailedString() {
48 return toString();
49 }
50 }
51
52
53
54 public class SimpleGlyph extends Glyph {
55
56 private static final int ON_CURVE = 0;
57
58 private static final int X_SHORT = 1;
59
60 private static final int Y_SHORT = 2;
61
62 private static final int REPEAT_FLAG = 3;
63
64 private static final int X_SAME = 4;
65
66 private static final int Y_SAME = 5;
67
68 private static final int X_POSITIVE = 4;
69
70 private static final int Y_POSITIVE = 5;
71
72 public int numberOfContours;
73
74 public int[] endPtsOfContours;
75
76 public int[] instructions;
77
78 public int[] flags;
79
80 public int[] xCoordinates, yCoordinates;
81
82 public boolean[] onCurve;
83
84 public GeneralPath shape;
85
86 public SimpleGlyph(int numberOfContours) {
87 this.numberOfContours = numberOfContours;
88 this.endPtsOfContours = new int[numberOfContours];
89 }
90
91 public String getType() {
92 return "Simple Glyph";
93 }
94
95 public void read() throws IOException {
96 super.read();
97
98 for (int i = 0; i < endPtsOfContours.length; i++)
99 endPtsOfContours[i] = ttf.readUShort();
100
101 instructions = new int[ttf.readUShort()];
102 for (int i = 0; i < instructions.length; i++)
103 instructions[i] = ttf.readByte();
104
105 int numberOfPoints = endPtsOfContours[endPtsOfContours.length - 1] + 1;
106 flags = new int[numberOfPoints];
107 xCoordinates = new int[numberOfPoints];
108 yCoordinates = new int[numberOfPoints];
109 onCurve = new boolean[numberOfPoints];
110 int repeatCount = 0;
111 int repeatFlag = 0;
112 for (int i = 0; i < numberOfPoints; i++) {
113 if (repeatCount > 0) {
114 flags[i] = repeatFlag;
115 repeatCount--;
116 } else {
117 flags[i] = ttf.readRawByte();
118 if (TTFInput.flagBit(flags[i], REPEAT_FLAG)) {
119 repeatCount = ttf.readByte();
120 repeatFlag = flags[i];
121 }
122 }
123 TTFInput.checkZeroBit(flags[i], 6, "flags");
124 TTFInput.checkZeroBit(flags[i], 7, "flags");
125 onCurve[i] = TTFInput.flagBit(flags[i], ON_CURVE);
126 }
127
128 int last = 0;
129 for (int i = 0; i < numberOfPoints; i++) {
130 if (TTFInput.flagBit(flags[i], X_SHORT)) {
131 if (TTFInput.flagBit(flags[i], X_POSITIVE)) {
132 last = xCoordinates[i] = last + ttf.readByte();
133 } else {
134 last = xCoordinates[i] = last - ttf.readByte();
135 }
136 } else {
137 if (TTFInput.flagBit(flags[i], X_SAME)) {
138 last = xCoordinates[i] = last;
139 } else {
140 last = xCoordinates[i] = last + ttf.readShort();
141 }
142 }
143 }
144
145 last = 0;
146 for (int i = 0; i < numberOfPoints; i++) {
147 if (TTFInput.flagBit(flags[i], Y_SHORT)) {
148 if (TTFInput.flagBit(flags[i], Y_POSITIVE)) {
149 last = yCoordinates[i] = last + ttf.readByte();
150 } else {
151 last = yCoordinates[i] = last - ttf.readByte();
152 }
153 } else {
154 if (TTFInput.flagBit(flags[i], Y_SAME)) {
155 last = yCoordinates[i] = last;
156 } else {
157 last = yCoordinates[i] = last + ttf.readShort();
158 }
159 }
160 }
161 }
162
163 public String toString() {
164 String str = super.toString() + ", " + numberOfContours
165 + " contours, endPts={";
166 for (int i = 0; i < numberOfContours; i++)
167 str += (i == 0 ? "" : ",") + endPtsOfContours[i];
168 str += "}, " + instructions.length + " instructions";
169 return str;
170 }
171
172 public String toDetailedString() {
173 String str = toString() + "\n instructions = {";
174 for (int i = 0; i < instructions.length; i++) {
175 str += Integer.toHexString(instructions[i]) + " ";
176 }
177 return str + "}";
178 }
179
180 public GeneralPath getShape() {
181 if (shape != null) {
182 return shape;
183 }
184
185 shape = new GeneralPath(GeneralPath.WIND_NON_ZERO);
186 int p = 0;
187 for (int i = 0; i < endPtsOfContours.length; i++) {
188 int startIndex = p++;
189 shape.moveTo(xCoordinates[startIndex], yCoordinates[startIndex]);
190 boolean lastOnCurve = true;
191 while (p <= endPtsOfContours[i]) {
192
193 if (onCurve[p]) {
194 if (lastOnCurve) {
195 shape.lineTo(xCoordinates[p], yCoordinates[p]);
196 } else {
197 shape.quadTo(xCoordinates[p - 1], yCoordinates[p - 1],
198 xCoordinates[p], yCoordinates[p]);
199 }
200 lastOnCurve = true;
201 } else {
202 if (!lastOnCurve) {
203 int x1 = xCoordinates[p - 1];
204 int y1 = yCoordinates[p - 1];
205 int x2 = (int)((x1 + xCoordinates[p])/ 2.0);
206 int y2 = (int)((y1 + yCoordinates[p])/ 2.0);
207 shape.quadTo(x1, y1, x2, y2);
208 }
209 lastOnCurve = false;
210 }
211 p++;
212 }
213 if (!onCurve[p - 1]) {
214 shape.quadTo(xCoordinates[p - 1], yCoordinates[p - 1],
215 xCoordinates[startIndex], yCoordinates[startIndex]);
216 } else if ((xCoordinates[p - 1] != xCoordinates[startIndex]) ||
217 (yCoordinates[p - 1] != yCoordinates[startIndex])) {
218 shape.closePath();
219 }
220 }
221 return shape;
222 }
223 }
224
225
226
227 public class CompositeGlyph extends Glyph {
228
229 private static final int ARGS_WORDS = 0;
230
231 private static final int ARGS_XY = 1;
232
233 private static final int SCALE = 3;
234
235 private static final int XY_SCALE = 6;
236
237 private static final int TWO_BY_TWO = 7;
238
239 private static final int MORE_COMPONENTS = 5;
240
241 private GeneralPath shape;
242
243 private int noComponents;
244
245 public String getType() {
246 return "Composite Glyph";
247 }
248
249 public GeneralPath getShape() {
250 return shape;
251 }
252
253 public void read() throws IOException {
254 super.read();
255 shape = new GeneralPath();
256
257 noComponents = 0;
258 boolean more = true;
259 while (more) {
260 noComponents++;
261 ttf.readUShortFlags();
262 more = ttf.flagBit(MORE_COMPONENTS);
263 int glyphIndex = ttf.readUShort();
264 int arg1, arg2;
265 if (ttf.flagBit(ARGS_WORDS)) {
266 arg1 = ttf.readShort();
267 arg2 = ttf.readShort();
268 } else {
269 arg1 = ttf.readChar();
270 arg2 = ttf.readChar();
271 }
272 AffineTransform t = new AffineTransform();
273 if (ttf.flagBit(ARGS_XY)) {
274 t.translate(arg1, arg2);
275 } else {
276 System.err
277 .println("TTFGlyfTable: ARGS_ARE_POINTS not implemented.");
278 }
279
280 if (ttf.flagBit(SCALE)) {
281 double scale = ttf.readF2Dot14();
282 t.scale(scale, scale);
283 } else if (ttf.flagBit(XY_SCALE)) {
284 double scaleX = ttf.readF2Dot14();
285 double scaleY = ttf.readF2Dot14();
286 t.scale(scaleX, scaleY);
287 } else if (ttf.flagBit(TWO_BY_TWO)) {
288 System.err
289 .println("TTFGlyfTable: WE_HAVE_A_TWO_BY_TWO not implemented.");
290 }
291
292 GeneralPath appendGlyph = (GeneralPath) getGlyph(glyphIndex)
293 .getShape().clone();
294 appendGlyph.transform(t);
295 shape.append(appendGlyph, false);
296 }
297 }
298
299 public String toString() {
300 return super.toString() + ", " + noComponents + " components";
301 }
302
303 }
304
305
306
307 public Glyph[] glyphs;
308
309 private long[] offsets;
310
311 public String getTag() {
312 return "glyf";
313 }
314
315 public void readTable() throws IOException {
316 glyphs = new Glyph[((TTFMaxPTable) getTable("maxp")).numGlyphs];
317 offsets = ((TTFLocaTable) getTable("loca")).offset;
318
319 if (READ_GLYPHS) {
320 for (int i = 0; i < glyphs.length; i++) {
321 if ((i > 0) && (offsets[i - 1] == offsets[i])) {
322 glyphs[i] = glyphs[i - 1];
323 } else {
324 try {
325 getGlyph(i);
326 } catch (IOException e) {
327 System.err.println("While reading glyph #" + i
328 + " (offset " + offsets[i] + "):");
329 e.printStackTrace();
330 }
331 }
332 }
333 }
334
335 }
336
337 public Glyph getGlyph(int i) throws IOException {
338 if (glyphs[i] != null) {
339 return glyphs[i];
340 } else {
341 ttf.pushPos();
342 ttf.seek(offsets[i]);
343 int numberOfContours = ttf.readShort();
344 if (numberOfContours >= 0)
345 glyphs[i] = new SimpleGlyph(numberOfContours);
346 else
347 glyphs[i] = new CompositeGlyph();
348 glyphs[i].read();
349
350 ttf.popPos();
351 return glyphs[i];
352 }
353 }
354
355 public String toString() {
356 String str = super.toString();
357 for (int i = 0; i < glyphs.length; i++)
358 str += "\n #" + i + ": " + glyphs[i];
359 return str;
360 }
361 }