001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.font.afm;
031:
032: import java.io.IOException;
033: import java.io.InputStream;
034:
035: /**
036: * A simple parser for AFM type definition files.
037: *
038: * <p>
039: * See the "Adobe Font Metrics File Format Specification"
040: * </p>
041: */
042: public class AFMParser {
043: private static String TOKEN_Comment = "Comment"; //$NON-NLS-1$
044:
045: private static String TOKEN_EndCharMetrics = "EndCharMetrics"; //$NON-NLS-1$
046:
047: // generic tokens
048: private static String TOKEN_EndFlag = "End"; //$NON-NLS-1$
049:
050: private static String TOKEN_EndFontMetrics = "EndFontMetrics"; //$NON-NLS-1$
051:
052: public static String TOKEN_FontName = "FontName"; //$NON-NLS-1$
053:
054: private static String TOKEN_StartCharMetrics = "StartCharMetrics"; //$NON-NLS-1$
055:
056: /*
057: * currently unused tokens
058: *
059: * private static String TOKEN_FullName = "FullName"; private static String
060: * TOKEN_FamilyName = "FamilyName"; private static String TOKEN_Weight =
061: * "Weight"; private static String TOKEN_ItalicAngle = "ItalicAngle";
062: * private static String TOKEN_IsFixedPitch = "IsFixedPitch"; private static
063: * String TOKEN_FontBBox = "FontBBox"; private static String
064: * TOKEN_UnderlinePosition = "UnderlinePosition"; private static String
065: * TOKEN_UnderlineThickness = "UnderlineThickness"; private static String
066: * TOKEN_Version = "Version"; private static String TOKEN_Notice = "Notice";
067: * private static String TOKEN_EncodingScheme = "EncodingScheme"; private
068: * static String TOKEN_CapHeight = "CapHeight"; private static String
069: * TOKEN_XHeight = "XHeight"; private static String TOKEN_Ascender =
070: * "Ascender"; private static String TOKEN_Descender = "Descender"; private
071: * static String TOKEN_StartComposites = "StartComposites"; private static
072: * String TOKEN_EndComposites = "EndComposites";
073: */
074: private static String TOKEN_StartFlag = "Start"; //$NON-NLS-1$
075:
076: /*
077: * todo under construction
078: */
079: private static String TOKEN_StartFontMetrics = "StartFontMetrics"; //$NON-NLS-1$
080:
081: private InputStream inputStream;
082:
083: //
084: private AFM result;
085:
086: /**
087: * AFMParser constructor comment.
088: */
089: public AFMParser() {
090: super ();
091: }
092:
093: /**
094: * docme
095: *
096: * @return docme
097: */
098: protected InputStream getInputStream() {
099: return inputStream;
100: }
101:
102: /**
103: * The {@link AFM} object parsed from the stream.
104: *
105: * @return The {@link AFM} object parsed from the stream.
106: */
107: public AFM getResult() {
108: return result;
109: }
110:
111: /**
112: * Parse a {@link AFM} object from the input stream <code>is</code>.
113: *
114: * @param is
115: * The input stream containing the definition.
116: *
117: * @return The {@link AFM } parsed.
118: *
119: * @throws IOException
120: */
121: public AFM parse(InputStream is) throws IOException {
122: AFM afm = new AFM();
123: setResult(afm);
124: setInputStream(is);
125: read();
126: setInputStream(null);
127: return getResult();
128: }
129:
130: /**
131: * docme
132: *
133: * @throws IOException
134: * docme
135: */
136: protected void read() throws IOException {
137: String token;
138:
139: // todo do not define readToken as string
140: token = readToken();
141:
142: if (!token.equals(TOKEN_StartFontMetrics)) {
143: throw new IOException("afm stream does not start with " //$NON-NLS-1$
144: + TOKEN_StartFontMetrics);
145: }
146:
147: readToken();
148: token = readFontMetrics();
149:
150: if ((token == null) || !token.equals(TOKEN_EndFontMetrics)) {
151: throw new IOException("afm stream does not end with " //$NON-NLS-1$
152: + TOKEN_EndFontMetrics);
153: }
154: }
155:
156: /**
157: * ignore this and any nested Start/End pair
158: *
159: * @return docme
160: *
161: * @throws IOException
162: * docme
163: */
164: protected String readBlock() throws IOException {
165: String line;
166: line = readLine();
167:
168: while ((line != null) && !line.startsWith(TOKEN_EndFlag)) {
169: if (line.startsWith(TOKEN_Comment)) {
170: // ignore
171: } else {
172: if (line.startsWith(TOKEN_StartFlag)) {
173: line = readBlock();
174: }
175: }
176:
177: line = readLine();
178: }
179:
180: return line;
181: }
182:
183: /**
184: * docme
185: *
186: * @return docme
187: *
188: * @throws IOException
189: * docme
190: */
191: protected String readCharMetrics() throws IOException {
192: String line;
193:
194: // read count
195: readToken();
196: line = readLine();
197:
198: while ((line != null) && !line.startsWith(TOKEN_EndCharMetrics)) {
199: getResult().addChar(AFMChar.create(line));
200: line = readLine();
201: }
202:
203: return line;
204: }
205:
206: /**
207: * docme
208: *
209: * @return docme
210: *
211: * @throws IOException
212: * docme
213: */
214: protected String readFontMetrics() throws IOException {
215: String token;
216: token = readToken();
217:
218: while ((token != null) && !token.equals(TOKEN_EndFontMetrics)) {
219: if (token.equals(TOKEN_Comment)) {
220: // ignore line?
221: } else {
222: if (token.equals(TOKEN_StartCharMetrics)) {
223: token = readCharMetrics();
224: } else {
225: if (token.startsWith(TOKEN_StartFlag)) {
226: token = readBlock();
227: } else {
228: getResult().setAttribute(token, readLine());
229: }
230: }
231: }
232:
233: token = readToken();
234: }
235:
236: return token;
237: }
238:
239: /**
240: * docme
241: *
242: * @return docme
243: *
244: * @throws IOException
245: * docme
246: */
247: protected String readLine() throws IOException {
248: int i;
249: int count;
250: byte[] token;
251:
252: count = 0;
253: inputStream.mark(Integer.MAX_VALUE);
254: i = inputStream.read();
255: while ((i > -1) && ((i == '\n') || (i == '\r'))) {
256: inputStream.mark(Integer.MAX_VALUE);
257: i = inputStream.read();
258: }
259:
260: while ((i > -1) && !((i == '\n') || (i == '\r'))) {
261: count++;
262: i = inputStream.read();
263: }
264:
265: if (count == 0) {
266: return null;
267: }
268:
269: token = new byte[count];
270: inputStream.reset();
271: inputStream.read(token);
272: inputStream.skip(1);
273: return new String(token);
274: }
275:
276: /**
277: * docme
278: *
279: * @return docme
280: *
281: * @throws IOException
282: * docme
283: */
284: protected String readToken() throws IOException {
285: int i;
286: int count;
287: byte[] token;
288:
289: count = 0;
290: inputStream.mark(Integer.MAX_VALUE);
291: i = inputStream.read();
292: while ((i > -1)
293: && ((i == ' ') || (i == '\t') || (i == '\n') || (i == '\r'))) {
294: inputStream.mark(Integer.MAX_VALUE);
295: i = inputStream.read();
296: }
297:
298: while ((i > -1)
299: && !((i == ' ') || (i == '\t') || (i == '\n') || (i == '\r'))) {
300: count++;
301: i = inputStream.read();
302: }
303:
304: if (count == 0) {
305: return null;
306: }
307:
308: token = new byte[count];
309: inputStream.reset();
310: inputStream.read(token);
311: inputStream.skip(1);
312: return new String(token);
313: }
314:
315: /**
316: * docme
317: *
318: * @param newInputStream
319: * docme
320: */
321: private void setInputStream(java.io.InputStream newInputStream) {
322: inputStream = newInputStream;
323: }
324:
325: /**
326: * docme
327: *
328: * @param newResult
329: * docme
330: */
331: private void setResult(AFM newResult) {
332: result = newResult;
333: }
334: }
|