KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > pdfbox > afmparser > AFMParser


1 /**
2  * Copyright (c) 2003, www.pdfbox.org
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  * 3. Neither the name of pdfbox; nor the names of its
14  * contributors may be used to endorse or promote products derived from this
15  * software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * http://www.pdfbox.org
29  *
30  */

31 package org.pdfbox.afmparser;
32
33 import java.io.InputStream JavaDoc;
34 import java.io.IOException JavaDoc;
35
36 import java.util.StringTokenizer JavaDoc;
37
38 import org.pdfbox.afmtypes.Composite;
39 import org.pdfbox.afmtypes.CompositePart;
40 import org.pdfbox.afmtypes.CharMetric;
41 import org.pdfbox.afmtypes.FontMetric;
42 import org.pdfbox.afmtypes.KernPair;
43 import org.pdfbox.afmtypes.Ligature;
44 import org.pdfbox.afmtypes.TrackKern;
45
46 import org.pdfbox.util.BoundingBox;
47
48 /**
49  * This class is used to parse AFM(Adobe Font Metrics) documents.
50  *
51  * @see <A HREF="http://partners.adobe.com/asn/developer/type/">AFM Documentation</A>
52  *
53  * @author Ben Litchfield (ben@csh.rit.edu)
54  * @version $Revision: 1.8 $
55  */

56 public class AFMParser
57 {
58     /**
59      * This is a comment in a AFM file.
60      */

61     public static final String JavaDoc COMMENT = "Comment";
62     /**
63      * This is the constant used in the AFM file to start a font metrics item.
64      */

65     public static final String JavaDoc START_FONT_METRICS = "StartFontMetrics";
66     /**
67      * This is the constant used in the AFM file to end a font metrics item.
68      */

69     public static final String JavaDoc END_FONT_METRICS = "EndFontMetrics";
70     /**
71      * This is the font name.
72      */

73     public static final String JavaDoc FONT_NAME = "FontName";
74     /**
75      * This is the full name.
76      */

77     public static final String JavaDoc FULL_NAME = "FullName";
78     /**
79      * This is the Family name.
80      */

81     public static final String JavaDoc FAMILY_NAME = "FamilyName";
82     /**
83      * This is the weight.
84      */

85     public static final String JavaDoc WEIGHT = "Weight";
86     /**
87      * This is the font bounding box.
88      */

89     public static final String JavaDoc FONT_BBOX = "FontBBox";
90     /**
91      * This is the version of the font.
92      */

93     public static final String JavaDoc VERSION = "Version";
94     /**
95      * This is the notice.
96      */

97     public static final String JavaDoc NOTICE = "Notice";
98     /**
99      * This is the encoding scheme.
100      */

101     public static final String JavaDoc ENCODING_SCHEME = "EncodingScheme";
102     /**
103      * This is the mapping scheme.
104      */

105     public static final String JavaDoc MAPPING_SCHEME = "MappingScheme";
106     /**
107      * This is the escape character.
108      */

109     public static final String JavaDoc ESC_CHAR = "EscChar";
110     /**
111      * This is the character set.
112      */

113     public static final String JavaDoc CHARACTER_SET = "CharacterSet";
114     /**
115      * This is the characters attribute.
116      */

117     public static final String JavaDoc CHARACTERS = "Characters";
118     /**
119      * This will determine if this is a base font.
120      */

121     public static final String JavaDoc IS_BASE_FONT = "IsBaseFont";
122     /**
123      * This is the V Vector attribute.
124      */

125     public static final String JavaDoc V_VECTOR = "VVector";
126     /**
127      * This will tell if the V is fixed.
128      */

129     public static final String JavaDoc IS_FIXED_V = "IsFixedV";
130     /**
131      * This is the cap height attribute.
132      */

133     public static final String JavaDoc CAP_HEIGHT = "CapHeight";
134     /**
135      * This is the X height.
136      */

137     public static final String JavaDoc X_HEIGHT = "XHeight";
138     /**
139      * This is ascender attribute.
140      */

141     public static final String JavaDoc ASCENDER = "Ascender";
142     /**
143      * This is the descender attribute.
144      */

145     public static final String JavaDoc DESCENDER = "Descender";
146
147     /**
148      * The underline position.
149      */

150     public static final String JavaDoc UNDERLINE_POSITION = "UnderlinePosition";
151     /**
152      * This is the Underline thickness.
153      */

154     public static final String JavaDoc UNDERLINE_THICKNESS = "UnderlineThickness";
155     /**
156      * This is the italic angle.
157      */

158     public static final String JavaDoc ITALIC_ANGLE = "ItalicAngle";
159     /**
160      * This is the char width.
161      */

162     public static final String JavaDoc CHAR_WIDTH = "CharWidth";
163     /**
164      * This will determine if this is fixed pitch.
165      */

166     public static final String JavaDoc IS_FIXED_PITCH = "IsFixedPitch";
167     /**
168      * This is the start of character metrics.
169      */

170     public static final String JavaDoc START_CHAR_METRICS = "StartCharMetrics";
171     /**
172      * This is the end of character metrics.
173      */

174     public static final String JavaDoc END_CHAR_METRICS = "EndCharMetrics";
175     /**
176      * The character metrics c value.
177      */

178     public static final String JavaDoc CHARMETRICS_C = "C";
179     /**
180      * The character metrics c value.
181      */

182     public static final String JavaDoc CHARMETRICS_CH = "CH";
183     /**
184      * The character metrics value.
185      */

186     public static final String JavaDoc CHARMETRICS_WX = "WX";
187     /**
188      * The character metrics value.
189      */

190     public static final String JavaDoc CHARMETRICS_W0X = "W0X";
191     /**
192      * The character metrics value.
193      */

194     public static final String JavaDoc CHARMETRICS_W1X = "W1X";
195     /**
196      * The character metrics value.
197      */

198     public static final String JavaDoc CHARMETRICS_WY = "WY";
199     /**
200      * The character metrics value.
201      */

202     public static final String JavaDoc CHARMETRICS_W0Y = "W0Y";
203     /**
204      * The character metrics value.
205      */

206     public static final String JavaDoc CHARMETRICS_W1Y = "W1Y";
207     /**
208      * The character metrics value.
209      */

210     public static final String JavaDoc CHARMETRICS_W = "W";
211     /**
212      * The character metrics value.
213      */

214     public static final String JavaDoc CHARMETRICS_W0 = "W0";
215     /**
216      * The character metrics value.
217      */

218     public static final String JavaDoc CHARMETRICS_W1 = "W1";
219     /**
220      * The character metrics value.
221      */

222     public static final String JavaDoc CHARMETRICS_VV = "VV";
223     /**
224      * The character metrics value.
225      */

226     public static final String JavaDoc CHARMETRICS_N = "N";
227     /**
228      * The character metrics value.
229      */

230     public static final String JavaDoc CHARMETRICS_B = "B";
231     /**
232      * The character metrics value.
233      */

234     public static final String JavaDoc CHARMETRICS_L = "L";
235     /**
236      * The character metrics value.
237      */

238     public static final String JavaDoc STD_HW = "StdHW";
239     /**
240      * The character metrics value.
241      */

242     public static final String JavaDoc STD_VW = "StdVW";
243     /**
244      * This is the start of track kern data.
245      */

246     public static final String JavaDoc START_TRACK_KERN = "StartTrackKern";
247     /**
248      * This is the end of track kern data.
249      */

250     public static final String JavaDoc END_TRACK_KERN = "EndTrackKern";
251     /**
252      * This is the start of kern data.
253      */

254     public static final String JavaDoc START_KERN_DATA = "StartKernData";
255     /**
256      * This is the end of kern data.
257      */

258     public static final String JavaDoc END_KERN_DATA = "EndKernData";
259     /**
260      * This is the start of kern pairs data.
261      */

262     public static final String JavaDoc START_KERN_PAIRS = "StartKernPairs";
263     /**
264      * This is the end of kern pairs data.
265      */

266     public static final String JavaDoc END_KERN_PAIRS = "EndKernPairs";
267     /**
268      * This is the start of kern pairs data.
269      */

270     public static final String JavaDoc START_KERN_PAIRS0 = "StartKernPairs0";
271     /**
272      * This is the start of kern pairs data.
273      */

274     public static final String JavaDoc START_KERN_PAIRS1 = "StartKernPairs1";
275     /**
276      * This is the start compisites data section.
277      */

278     public static final String JavaDoc START_COMPOSITES = "StartComposites";
279     /**
280      * This is the end compisites data section.
281      */

282     public static final String JavaDoc END_COMPOSITES = "EndComposites";
283     /**
284      * This is a composite character.
285      */

286     public static final String JavaDoc CC = "CC";
287     /**
288      * This is a composite charater part.
289      */

290     public static final String JavaDoc PCC = "PCC";
291     /**
292      * This is a kern pair.
293      */

294     public static final String JavaDoc KERN_PAIR_KP = "KP";
295     /**
296      * This is a kern pair.
297      */

298     public static final String JavaDoc KERN_PAIR_KPH = "KPH";
299     /**
300      * This is a kern pair.
301      */

302     public static final String JavaDoc KERN_PAIR_KPX = "KPX";
303     /**
304      * This is a kern pair.
305      */

306     public static final String JavaDoc KERN_PAIR_KPY = "KPY";
307
308     private static final int BITS_IN_HEX = 16;
309
310
311     private InputStream JavaDoc input;
312     private FontMetric result;
313
314     /**
315      * A method to test parsing of all AFM documents in the resources
316      * directory.
317      *
318      * @param args Ignored.
319      *
320      * @throws IOException If there is an error parsing one of the documents.
321      */

322     public static void main( String JavaDoc[] args ) throws IOException JavaDoc
323     {
324         java.io.File JavaDoc afmDir = new java.io.File JavaDoc( "Resources/afm" );
325         java.io.File JavaDoc[] files = afmDir.listFiles();
326         for( int i=0; i< files.length; i++ )
327         {
328             if( files[i].getPath().endsWith( ".AFM" ) )
329             {
330                 long start = System.currentTimeMillis();
331                 java.io.FileInputStream JavaDoc input = new java.io.FileInputStream JavaDoc( files[i] );
332                 AFMParser parser = new AFMParser( input );
333                 parser.parse();
334                 long stop = System.currentTimeMillis();
335                 System.out.println( "Parsing:" + files[i].getPath() + " " + (stop-start) );
336             }
337         }
338     }
339
340     /**
341      * Constructor.
342      *
343      * @param in The input stream to read the AFM document from.
344      */

345     public AFMParser( InputStream JavaDoc in )
346     {
347         input = in;
348     }
349
350     /**
351      * This will parse the AFM document. This will close the Input stream
352      * when the parsing is finished.
353      *
354      * @throws IOException If there is an IO error reading the document.
355      */

356     public void parse() throws IOException JavaDoc
357     {
358         result = parseFontMetric();
359     }
360
361     /**
362      * This will get the result of the parsing.
363      *
364      * @return The parsed java object.
365      */

366     public FontMetric getResult()
367     {
368         return result;
369     }
370
371     /**
372      * This will parse a font metrics item.
373      *
374      * @return The parse font metrics item.
375      *
376      * @throws IOException If there is an error reading the AFM file.
377      */

378     private FontMetric parseFontMetric() throws IOException JavaDoc
379     {
380         FontMetric fontMetrics = new FontMetric();
381         String JavaDoc startFontMetrics = readString();
382         if( !START_FONT_METRICS.equals( startFontMetrics ) )
383         {
384             throw new IOException JavaDoc( "Error: The AFM file should start with " + START_FONT_METRICS +
385                                    " and not '" + startFontMetrics + "'" );
386         }
387         fontMetrics.setAFMVersion( readFloat() );
388         String JavaDoc nextCommand = null;
389         while( !END_FONT_METRICS.equals( (nextCommand = readString() ) ) )
390         {
391             if( FONT_NAME.equals( nextCommand ) )
392             {
393                 fontMetrics.setFontName( readLine() );
394             }
395             else if( FULL_NAME.equals( nextCommand ) )
396             {
397                 fontMetrics.setFullName( readLine() );
398             }
399             else if( FAMILY_NAME.equals( nextCommand ) )
400             {
401                 fontMetrics.setFamilyName( readLine() );
402             }
403             else if( WEIGHT.equals( nextCommand ) )
404             {
405                 fontMetrics.setWeight( readLine() );
406             }
407             else if( FONT_BBOX.equals( nextCommand ) )
408             {
409                 BoundingBox bBox = new BoundingBox();
410                 bBox.setLowerLeftX( readFloat() );
411                 bBox.setLowerLeftY( readFloat() );
412                 bBox.setUpperRightX( readFloat() );
413                 bBox.setUpperRightY( readFloat() );
414                 fontMetrics.setFontBBox( bBox );
415             }
416             else if( VERSION.equals( nextCommand ) )
417             {
418                 fontMetrics.setFontVersion( readLine() );
419             }
420             else if( NOTICE.equals( nextCommand ) )
421             {
422                 fontMetrics.setNotice( readLine() );
423             }
424             else if( ENCODING_SCHEME.equals( nextCommand ) )
425             {
426                 fontMetrics.setEncodingScheme( readLine() );
427             }
428             else if( MAPPING_SCHEME.equals( nextCommand ) )
429             {
430                 fontMetrics.setMappingScheme( readInt() );
431             }
432             else if( ESC_CHAR.equals( nextCommand ) )
433             {
434                 fontMetrics.setEscChar( readInt() );
435             }
436             else if( CHARACTER_SET.equals( nextCommand ) )
437             {
438                 fontMetrics.setCharacterSet( readLine() );
439             }
440             else if( CHARACTERS.equals( nextCommand ) )
441             {
442                 fontMetrics.setCharacters( readInt() );
443             }
444             else if( IS_BASE_FONT.equals( nextCommand ) )
445             {
446                 fontMetrics.setIsBaseFont( readBoolean() );
447             }
448             else if( V_VECTOR.equals( nextCommand ) )
449             {
450                 float[] vector = new float[2];
451                 vector[0] = readFloat();
452                 vector[1] = readFloat();
453                 fontMetrics.setVVector( vector );
454             }
455             else if( IS_FIXED_V.equals( nextCommand ) )
456             {
457                 fontMetrics.setIsFixedV( readBoolean() );
458             }
459             else if( CAP_HEIGHT.equals( nextCommand ) )
460             {
461                 fontMetrics.setCapHeight( readFloat() );
462             }
463             else if( X_HEIGHT.equals( nextCommand ) )
464             {
465                 fontMetrics.setXHeight( readFloat() );
466             }
467             else if( ASCENDER.equals( nextCommand ) )
468             {
469                 fontMetrics.setAscender( readFloat() );
470             }
471             else if( DESCENDER.equals( nextCommand ) )
472             {
473                 fontMetrics.setDescender( readFloat() );
474             }
475             else if( STD_HW.equals( nextCommand ) )
476             {
477                 fontMetrics.setStandardHorizontalWidth( readFloat() );
478             }
479             else if( STD_VW.equals( nextCommand ) )
480             {
481                 fontMetrics.setStandardVerticalWidth( readFloat() );
482             }
483             else if( COMMENT.equals( nextCommand ) )
484             {
485                 fontMetrics.addComment( readLine() );
486             }
487             else if( UNDERLINE_POSITION.equals( nextCommand ) )
488             {
489                 fontMetrics.setUnderlinePosition( readFloat() );
490             }
491             else if( UNDERLINE_THICKNESS.equals( nextCommand ) )
492             {
493                 fontMetrics.setUnderlineThickness( readFloat() );
494             }
495             else if( ITALIC_ANGLE.equals( nextCommand ) )
496             {
497                 fontMetrics.setItalicAngle( readFloat() );
498             }
499             else if( CHAR_WIDTH.equals( nextCommand ) )
500             {
501                 float[] widths = new float[2];
502                 widths[0] = readFloat();
503                 widths[1] = readFloat();
504                 fontMetrics.setCharWidth( widths );
505             }
506             else if( IS_FIXED_PITCH.equals( nextCommand ) )
507             {
508                 fontMetrics.setFixedPitch( readBoolean() );
509             }
510             else if( START_CHAR_METRICS.equals( nextCommand ) )
511             {
512                 int count = readInt();
513                 for( int i=0; i<count; i++ )
514                 {
515                     CharMetric charMetric = parseCharMetric();
516                     fontMetrics.addCharMetric( charMetric );
517                 }
518                 String JavaDoc end = readString();
519                 if( !end.equals( END_CHAR_METRICS ) )
520                 {
521                     throw new IOException JavaDoc( "Error: Expected '" + END_CHAR_METRICS + "' actual '" +
522                                                 end + "'" );
523                 }
524             }
525             else if( START_COMPOSITES.equals( nextCommand ) )
526             {
527                 int count = readInt();
528                 for( int i=0; i<count; i++ )
529                 {
530                     Composite part = parseComposite();
531                     fontMetrics.addComposite( part );
532                 }
533                 String JavaDoc end = readString();
534                 if( !end.equals( END_COMPOSITES ) )
535                 {
536                     throw new IOException JavaDoc( "Error: Expected '" + END_COMPOSITES + "' actual '" +
537                                                 end + "'" );
538                 }
539             }
540             else if( START_KERN_DATA.equals( nextCommand ) )
541             {
542                 parseKernData( fontMetrics );
543             }
544             else
545             {
546                 throw new IOException JavaDoc( "Unknown AFM key '" + nextCommand + "'" );
547             }
548         }
549         return fontMetrics;
550     }
551
552     /**
553      * This will parse the kern data.
554      *
555      * @param fontMetrics The metrics class to put the parsed data into.
556      *
557      * @throws IOException If there is an error parsing the data.
558      */

559     private void parseKernData( FontMetric fontMetrics ) throws IOException JavaDoc
560     {
561         String JavaDoc nextCommand = null;
562         while( !(nextCommand = readString()).equals( END_KERN_DATA ) )
563         {
564             if( START_TRACK_KERN.equals( nextCommand ) )
565             {
566                 int count = readInt();
567                 for( int i=0; i<count; i++ )
568                 {
569                     TrackKern kern = new TrackKern();
570                     kern.setDegree( readInt() );
571                     kern.setMinPointSize( readFloat() );
572                     kern.setMinKern( readFloat() );
573                     kern.setMaxPointSize( readFloat() );
574                     kern.setMaxKern( readFloat() );
575                     fontMetrics.addTrackKern( kern );
576                 }
577                 String JavaDoc end = readString();
578                 if( !end.equals( END_TRACK_KERN ) )
579                 {
580                     throw new IOException JavaDoc( "Error: Expected '" + END_TRACK_KERN + "' actual '" +
581                                                 end + "'" );
582                 }
583             }
584             else if( START_KERN_PAIRS.equals( nextCommand ) )
585             {
586                 int count = readInt();
587                 for( int i=0; i<count; i++ )
588                 {
589                     KernPair pair = parseKernPair();
590                     fontMetrics.addKernPair( pair );
591                 }
592                 String JavaDoc end = readString();
593                 if( !end.equals( END_KERN_PAIRS ) )
594                 {
595                     throw new IOException JavaDoc( "Error: Expected '" + END_KERN_PAIRS + "' actual '" +
596                                                 end + "'" );
597                 }
598             }
599             else if( START_KERN_PAIRS0.equals( nextCommand ) )
600             {
601                 int count = readInt();
602                 for( int i=0; i<count; i++ )
603                 {
604                     KernPair pair = parseKernPair();
605                     fontMetrics.addKernPair0( pair );
606                 }
607                 String JavaDoc end = readString();
608                 if( !end.equals( END_KERN_PAIRS ) )
609                 {
610                     throw new IOException JavaDoc( "Error: Expected '" + END_KERN_PAIRS + "' actual '" +
611                                                 end + "'" );
612                 }
613             }
614             else if( START_KERN_PAIRS1.equals( nextCommand ) )
615             {
616                 int count = readInt();
617                 for( int i=0; i<count; i++ )
618                 {
619                     KernPair pair = parseKernPair();
620                     fontMetrics.addKernPair1( pair );
621                 }
622                 String JavaDoc end = readString();
623                 if( !end.equals( END_KERN_PAIRS ) )
624                 {
625                     throw new IOException JavaDoc( "Error: Expected '" + END_KERN_PAIRS + "' actual '" +
626                                                 end + "'" );
627                 }
628             }
629             else
630             {
631                 throw new IOException JavaDoc( "Unknown kerning data type '" + nextCommand + "'" );
632             }
633         }
634     }
635
636     /**
637      * This will parse a kern pair from the data stream.
638      *
639      * @return The kern pair that was parsed from the stream.
640      *
641      * @throws IOException If there is an error reading from the stream.
642      */

643     private KernPair parseKernPair() throws IOException JavaDoc
644     {
645         KernPair kernPair = new KernPair();
646         String JavaDoc cmd = readString();
647         if( KERN_PAIR_KP.equals( cmd ) )
648         {
649             String JavaDoc first = readString();
650             String JavaDoc second = readString();
651             float x = readFloat();
652             float y = readFloat();
653             kernPair.setFirstKernCharacter( first );
654             kernPair.setSecondKernCharacter( second );
655             kernPair.setX( x );
656             kernPair.setY( y );
657         }
658         else if( KERN_PAIR_KPH.equals( cmd ) )
659         {
660             String JavaDoc first = hexToString( readString() );
661             String JavaDoc second = hexToString( readString() );
662             float x = readFloat();
663             float y = readFloat();
664             kernPair.setFirstKernCharacter( first );
665             kernPair.setSecondKernCharacter( second );
666             kernPair.setX( x );
667             kernPair.setY( y );
668         }
669         else if( KERN_PAIR_KPX.equals( cmd ) )
670         {
671             String JavaDoc first = readString();
672             String JavaDoc second = readString();
673             float x = readFloat();
674             kernPair.setFirstKernCharacter( first );
675             kernPair.setSecondKernCharacter( second );
676             kernPair.setX( x );
677             kernPair.setY( 0 );
678         }
679         else if( KERN_PAIR_KPY.equals( cmd ) )
680         {
681             String JavaDoc first = readString();
682             String JavaDoc second = readString();
683             float y = readFloat();
684             kernPair.setFirstKernCharacter( first );
685             kernPair.setSecondKernCharacter( second );
686             kernPair.setX( 0 );
687             kernPair.setY( y );
688         }
689         else
690         {
691             throw new IOException JavaDoc( "Error expected kern pair command actual='" + cmd + "'" );
692         }
693         return kernPair;
694     }
695
696     /**
697      * This will convert and angle bracket hex string to a string.
698      *
699      * @param hexString An angle bracket string.
700      *
701      * @return The bytes of the hex string.
702      *
703      * @throws IOException If the string is in an invalid format.
704      */

705     private String JavaDoc hexToString( String JavaDoc hexString ) throws IOException JavaDoc
706     {
707         if( hexString.length() < 2 )
708         {
709             throw new IOException JavaDoc( "Error: Expected hex string of length >= 2 not='" + hexString );
710         }
711         if( hexString.charAt( 0 ) != '<' ||
712             hexString.charAt( hexString.length() -1 ) != '>' )
713         {
714             throw new IOException JavaDoc( "String should be enclosed by angle brackets '" + hexString+ "'" );
715         }
716         hexString = hexString.substring( 1, hexString.length() -1 );
717         byte[] data = new byte[ (int)(hexString.length() / 2) ];
718         for( int i=0; i<hexString.length(); i+=2 )
719         {
720             String JavaDoc hex = "" + hexString.charAt( i ) + hexString.charAt( i+1 );
721             try
722             {
723                 data[ i / 2 ] = (byte)Integer.parseInt( hex, BITS_IN_HEX );
724             }
725             catch( NumberFormatException JavaDoc e )
726             {
727                 throw new IOException JavaDoc( "Error parsing AFM file:" + e );
728             }
729         }
730         return new String JavaDoc( data );
731     }
732
733     /**
734      * This will parse a composite part from the stream.
735      *
736      * @return The composite.
737      *
738      * @throws IOException If there is an error parsing the composite.
739      */

740     private Composite parseComposite() throws IOException JavaDoc
741     {
742         Composite composite = new Composite();
743         String JavaDoc partData = readLine();
744         StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc( partData, " ;" );
745
746
747         String JavaDoc cc = tokenizer.nextToken();
748         if( !cc.equals( CC ) )
749         {
750             throw new IOException JavaDoc( "Expected '" + CC + "' actual='" + cc + "'" );
751         }
752         String JavaDoc name = tokenizer.nextToken();
753         composite.setName( name );
754
755         int partCount;
756         try
757         {
758             partCount = Integer.parseInt( tokenizer.nextToken() );
759         }
760         catch( NumberFormatException JavaDoc e )
761         {
762             throw new IOException JavaDoc( "Error parsing AFM document:" + e );
763         }
764         for( int i=0; i<partCount; i++ )
765         {
766             CompositePart part = new CompositePart();
767             String JavaDoc pcc = tokenizer.nextToken();
768             if( !pcc.equals( PCC ) )
769             {
770                 throw new IOException JavaDoc( "Expected '" + PCC + "' actual='" + pcc + "'" );
771             }
772             String JavaDoc partName = tokenizer.nextToken();
773             try
774             {
775                 int x = Integer.parseInt( tokenizer.nextToken() );
776                 int y = Integer.parseInt( tokenizer.nextToken() );
777
778                 part.setName( partName );
779                 part.setXDisplacement( x );
780                 part.setYDisplacement( y );
781                 composite.addPart( part );
782             }
783             catch( NumberFormatException JavaDoc e )
784             {
785                 throw new IOException JavaDoc( "Error parsing AFM document:" + e );
786             }
787         }
788         return composite;
789     }
790
791     /**
792      * This will parse a single CharMetric object from the stream.
793      *
794      * @return The next char metric in the stream.
795      *
796      * @throws IOException If there is an error reading from the stream.
797      */

798     private CharMetric parseCharMetric() throws IOException JavaDoc
799     {
800         CharMetric charMetric = new CharMetric();
801         String JavaDoc metrics = readLine();
802         StringTokenizer JavaDoc metricsTokenizer = new StringTokenizer JavaDoc( metrics );
803         try
804         {
805             while( metricsTokenizer.hasMoreTokens() )
806             {
807                 String JavaDoc nextCommand = metricsTokenizer.nextToken();
808                 if( nextCommand.equals( CHARMETRICS_C ) )
809                 {
810                     String JavaDoc charCode = metricsTokenizer.nextToken();
811                     charMetric.setCharacterCode( Integer.parseInt( charCode ) );
812                     verifySemicolon( metricsTokenizer );
813                 }
814                 else if( nextCommand.equals( CHARMETRICS_CH ) )
815                 {
816                     //Is the hex string <FF> or FF, the spec is a little
817
//unclear, wait and see if it breaks anything.
818
String JavaDoc charCode = metricsTokenizer.nextToken();
819                     charMetric.setCharacterCode( Integer.parseInt( charCode, BITS_IN_HEX ) );
820                     verifySemicolon( metricsTokenizer );
821                 }
822                 else if( nextCommand.equals( CHARMETRICS_WX ) )
823                 {
824                     String JavaDoc wx = metricsTokenizer.nextToken();
825                     charMetric.setWx( Float.parseFloat( wx ) );
826                     verifySemicolon( metricsTokenizer );
827                 }
828                 else if( nextCommand.equals( CHARMETRICS_W0X ) )
829                 {
830                     String JavaDoc w0x = metricsTokenizer.nextToken();
831                     charMetric.setW0x( Float.parseFloat( w0x ) );
832                     verifySemicolon( metricsTokenizer );
833                 }
834                 else if( nextCommand.equals( CHARMETRICS_W1X ) )
835                 {
836                     String JavaDoc w1x = metricsTokenizer.nextToken();
837                     charMetric.setW0x( Float.parseFloat( w1x ) );
838                     verifySemicolon( metricsTokenizer );
839                 }
840                 else if( nextCommand.equals( CHARMETRICS_WY ) )
841                 {
842                     String JavaDoc wy = metricsTokenizer.nextToken();
843                     charMetric.setWy( Float.parseFloat( wy ) );
844                     verifySemicolon( metricsTokenizer );
845                 }
846                 else if( nextCommand.equals( CHARMETRICS_W0Y ) )
847                 {
848                     String JavaDoc w0y = metricsTokenizer.nextToken();
849                     charMetric.setW0y( Float.parseFloat( w0y ) );
850                     verifySemicolon( metricsTokenizer );
851                 }
852                 else if( nextCommand.equals( CHARMETRICS_W1Y ) )
853                 {
854                     String JavaDoc w1y = metricsTokenizer.nextToken();
855                     charMetric.setW0y( Float.parseFloat( w1y ) );
856                     verifySemicolon( metricsTokenizer );
857                 }
858                 else if( nextCommand.equals( CHARMETRICS_W ) )
859                 {
860                     String JavaDoc w0 = metricsTokenizer.nextToken();
861                     String JavaDoc w1 = metricsTokenizer.nextToken();
862                     float[] w = new float[2];
863                     w[0] = Float.parseFloat( w0 );
864                     w[1] = Float.parseFloat( w1 );
865                     charMetric.setW( w );
866                     verifySemicolon( metricsTokenizer );
867                 }
868                 else if( nextCommand.equals( CHARMETRICS_W0 ) )
869                 {
870                     String JavaDoc w00 = metricsTokenizer.nextToken();
871                     String JavaDoc w01 = metricsTokenizer.nextToken();
872                     float[] w0 = new float[2];
873                     w0[0] = Float.parseFloat( w00 );
874                     w0[1] = Float.parseFloat( w01 );
875                     charMetric.setW0( w0 );
876                     verifySemicolon( metricsTokenizer );
877                 }
878                 else if( nextCommand.equals( CHARMETRICS_W1 ) )
879                 {
880                     String JavaDoc w10 = metricsTokenizer.nextToken();
881                     String JavaDoc w11 = metricsTokenizer.nextToken();
882                     float[] w1 = new float[2];
883                     w1[0] = Float.parseFloat( w10 );
884                     w1[1] = Float.parseFloat( w11 );
885                     charMetric.setW1( w1 );
886                     verifySemicolon( metricsTokenizer );
887                 }
888                 else if( nextCommand.equals( CHARMETRICS_VV ) )
889                 {
890                     String JavaDoc vv0 = metricsTokenizer.nextToken();
891                     String JavaDoc vv1 = metricsTokenizer.nextToken();
892                     float[] vv = new float[2];
893                     vv[0] = Float.parseFloat( vv0 );
894                     vv[1] = Float.parseFloat( vv1 );
895                     charMetric.setVv( vv );
896                     verifySemicolon( metricsTokenizer );
897                 }
898                 else if( nextCommand.equals( CHARMETRICS_N ) )
899                 {
900                     String JavaDoc name = metricsTokenizer.nextToken();
901                     charMetric.setName( name );
902                     verifySemicolon( metricsTokenizer );
903                 }
904                 else if( nextCommand.equals( CHARMETRICS_B ) )
905                 {
906                     String JavaDoc llx = metricsTokenizer.nextToken();
907                     String JavaDoc lly = metricsTokenizer.nextToken();
908                     String JavaDoc urx = metricsTokenizer.nextToken();
909                     String JavaDoc ury = metricsTokenizer.nextToken();
910                     BoundingBox box = new BoundingBox();
911                     box.setLowerLeftX( Float.parseFloat( llx ) );
912                     box.setLowerLeftY( Float.parseFloat( lly ) );
913                     box.setUpperRightX( Float.parseFloat( urx ) );
914                     box.setUpperRightY( Float.parseFloat( ury ) );
915                     charMetric.setBoundingBox( box );
916                     verifySemicolon( metricsTokenizer );
917                 }
918                 else if( nextCommand.equals( CHARMETRICS_L ) )
919                 {
920                     String JavaDoc successor = metricsTokenizer.nextToken();
921                     String JavaDoc ligature = metricsTokenizer.nextToken();
922                     Ligature lig = new Ligature();
923                     lig.setSuccessor( successor );
924                     lig.setLigature( ligature );
925                     charMetric.addLigature( lig );
926                     verifySemicolon( metricsTokenizer );
927                 }
928                 else
929                 {
930                     throw new IOException JavaDoc( "Unknown CharMetrics command '" + nextCommand + "'" );
931                 }
932             }
933         }
934         catch( NumberFormatException JavaDoc e )
935         {
936             throw new IOException JavaDoc( "Error: Corrupt AFM document:" + e );
937         }
938         return charMetric;
939     }
940
941     /**
942      * This is used to verify that a semicolon is the next token in the stream.
943      *
944      * @param tokenizer The tokenizer to read from.
945      *
946      * @throws IOException If the semicolon is missing.
947      */

948     private void verifySemicolon( StringTokenizer JavaDoc tokenizer ) throws IOException JavaDoc
949     {
950         if( tokenizer.hasMoreTokens() )
951         {
952             String JavaDoc semicolon = tokenizer.nextToken();
953             if( !semicolon.equals( ";" ) )
954             {
955                 throw new IOException JavaDoc( "Error: Expected semicolon in stream actual='" +
956                                             semicolon + "'" );
957             }
958         }
959         else
960         {
961             throw new IOException JavaDoc( "CharMetrics is missing a semicolon after a command" );
962         }
963     }
964
965     /**
966      * This will read a boolean from the stream.
967      *
968      * @return The boolean in the stream.
969      */

970     private boolean readBoolean() throws IOException JavaDoc
971     {
972         String JavaDoc theBoolean = readString();
973         return Boolean.valueOf( theBoolean ).booleanValue();
974     }
975
976     /**
977      * This will read an integer from the stream.
978      *
979      * @return The integer in the stream.
980      */

981     private int readInt() throws IOException JavaDoc
982     {
983         String JavaDoc theInt = readString();
984         try
985         {
986             return Integer.parseInt( theInt );
987         }
988         catch( NumberFormatException JavaDoc e )
989         {
990             throw new IOException JavaDoc( "Error parsing AFM document:" + e );
991         }
992     }
993
994     /**
995      * This will read a float from the stream.
996      *
997      * @return The float in the stream.
998      */

999     private float readFloat() throws IOException JavaDoc
1000    {
1001        String JavaDoc theFloat = readString();
1002        return Float.parseFloat( theFloat );
1003    }
1004
1005    /**
1006     * This will read until the end of a line.
1007     *
1008     * @return The string that is read.
1009     */

1010    private String JavaDoc readLine() throws IOException JavaDoc
1011    {
1012        //First skip the whitespace
1013
StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1014        int nextByte = input.read();
1015        while( isWhitespace( nextByte ) )
1016        {
1017            nextByte = input.read();
1018            //do nothing just skip the whitespace.
1019
}
1020        buf.append( (char)nextByte );
1021
1022        //now read the data
1023
while( !isEOL(nextByte = input.read()) )
1024        {
1025            buf.append( (char)nextByte );
1026        }
1027        return buf.toString();
1028    }
1029
1030    /**
1031     * This will read a string from the input stream and stop at any whitespace.
1032     *
1033     * @return The string read from the stream.
1034     *
1035     * @throws IOException If an IO error occurs when reading from the stream.
1036     */

1037    private String JavaDoc readString() throws IOException JavaDoc
1038    {
1039        //First skip the whitespace
1040
StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1041        int nextByte = input.read();
1042        while( isWhitespace( nextByte ) )
1043        {
1044            nextByte = input.read();
1045            //do nothing just skip the whitespace.
1046
}
1047        buf.append( (char)nextByte );
1048
1049        //now read the data
1050
while( !isWhitespace(nextByte = input.read()) )
1051        {
1052            buf.append( (char)nextByte );
1053        }
1054        return buf.toString();
1055    }
1056
1057    /**
1058     * This will determine if the byte is a whitespace character or not.
1059     *
1060     * @param character The character to test for whitespace.
1061     *
1062     * @return true If the character is whitespace as defined by the AFM spec.
1063     */

1064    private boolean isEOL( int character )
1065    {
1066        return character == 0x0D ||
1067               character == 0x0A;
1068    }
1069
1070    /**
1071     * This will determine if the byte is a whitespace character or not.
1072     *
1073     * @param character The character to test for whitespace.
1074     *
1075     * @return true If the character is whitespace as defined by the AFM spec.
1076     */

1077    private boolean isWhitespace( int character )
1078    {
1079        return character == (int)' ' ||
1080               character == (int)'\t' ||
1081               character == 0x0D ||
1082               character == 0x0A;
1083    }
1084}
Popular Tags