KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > pdfbox > pdmodel > font > PDFont


1 /**
2  * Copyright (c) 2003-2006, 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.pdmodel.font;
32
33 import org.fontbox.afm.AFMParser;
34
35 import org.fontbox.afm.FontMetric;
36
37 import org.fontbox.cmap.CMapParser;
38
39 import org.fontbox.cmap.CMap;
40
41 import org.pdfbox.encoding.AFMEncoding;
42 import org.pdfbox.encoding.DictionaryEncoding;
43 import org.pdfbox.encoding.Encoding;
44 import org.pdfbox.encoding.EncodingManager;
45
46 import org.pdfbox.cos.COSArray;
47 import org.pdfbox.cos.COSBase;
48 import org.pdfbox.cos.COSDictionary;
49 import org.pdfbox.cos.COSFloat;
50 import org.pdfbox.cos.COSName;
51 import org.pdfbox.cos.COSNumber;
52 import org.pdfbox.cos.COSStream;
53
54 import org.pdfbox.pdmodel.common.COSArrayList;
55 import org.pdfbox.pdmodel.common.COSObjectable;
56 import org.pdfbox.pdmodel.common.PDMatrix;
57 import org.pdfbox.pdmodel.common.PDRectangle;
58
59 import org.pdfbox.util.ResourceLoader;
60
61 import java.awt.Graphics JavaDoc;
62
63 import java.io.BufferedReader JavaDoc;
64 import java.io.InputStream JavaDoc;
65 import java.io.InputStreamReader JavaDoc;
66 import java.io.IOException JavaDoc;
67
68 import java.util.Collections JavaDoc;
69 import java.util.HashMap JavaDoc;
70 import java.util.List JavaDoc;
71 import java.util.Map JavaDoc;
72 import java.util.StringTokenizer JavaDoc;
73
74 /**
75  * This is the base class for all PDF fonts.
76  *
77  * @author <a HREF="mailto:ben@benlitchfield.com">Ben Litchfield</a>
78  * @version $Revision: 1.43 $
79  */

80 public abstract class PDFont implements COSObjectable
81 {
82
83     /**
84      * The cos dictionary for this font.
85      */

86     protected COSDictionary font;
87
88     /**
89      * This is only used if this is a font object and it has an encoding.
90      */

91     private Encoding fontEncoding = null;
92     /**
93      * This is only used if this is a font object and it has an encoding and it is
94      * a type0 font with a cmap.
95      */

96     private CMap cmap = null;
97
98     private static Map afmResources = null;
99     private static Map cmapObjects = null;
100     private static Map afmObjects = null;
101     private static Map cmapSubstitutions = null;
102
103     static
104     {
105         //these are read-only once they are created
106
afmResources = new HashMap JavaDoc();
107         cmapSubstitutions = new HashMap JavaDoc();
108         
109         //these are read-write
110
cmapObjects = Collections.synchronizedMap( new HashMap JavaDoc() );
111         afmObjects = Collections.synchronizedMap( new HashMap JavaDoc() );
112         
113
114         afmResources.put( COSName.getPDFName( "Courier-Bold" ), "Resources/afm/Courier-Bold.afm" );
115         afmResources.put( COSName.getPDFName( "Courier-BoldOblique" ), "Resources/afm/Courier-BoldOblique.afm" );
116         afmResources.put( COSName.getPDFName( "Courier" ), "Resources/afm/Courier.afm" );
117         afmResources.put( COSName.getPDFName( "Courier-Oblique" ), "Resources/afm/Courier-Oblique.afm" );
118         afmResources.put( COSName.getPDFName( "Helvetica" ), "Resources/afm/Helvetica.afm" );
119         afmResources.put( COSName.getPDFName( "Helvetica-Bold" ), "Resources/afm/Helvetica-Bold.afm" );
120         afmResources.put( COSName.getPDFName( "Helvetica-BoldOblique" ), "Resources/afm/Helvetica-BoldOblique.afm" );
121         afmResources.put( COSName.getPDFName( "Helvetica-Oblique" ), "Resources/afm/Helvetica-Oblique.afm" );
122         afmResources.put( COSName.getPDFName( "Symbol" ), "Resources/afm/Symbol.afm" );
123         afmResources.put( COSName.getPDFName( "Times-Bold" ), "Resources/afm/Times-Bold.afm" );
124         afmResources.put( COSName.getPDFName( "Times-BoldItalic" ), "Resources/afm/Times-BoldItalic.afm" );
125         afmResources.put( COSName.getPDFName( "Times-Italic" ), "Resources/afm/Times-Italic.afm" );
126         afmResources.put( COSName.getPDFName( "Times-Roman" ), "Resources/afm/Times-Roman.afm" );
127         afmResources.put( COSName.getPDFName( "ZapfDingbats" ), "Resources/afm/ZapfDingbats.afm" );
128
129         cmapSubstitutions.put( "ETen-B5-H", "ETen-B5-UCS2" );
130         cmapSubstitutions.put( "ETen-B5-V", "ETen-B5-UCS2" );
131         cmapSubstitutions.put( "ETenms-B5-H", "ETen-B5-UCS2" );
132         cmapSubstitutions.put( "ETenms-B5-V", "ETen-B5-UCS2" );
133         
134         cmapSubstitutions.put( "90ms-RKSJ-H", "90ms-RKSJ-UCS2" );
135         cmapSubstitutions.put( "90ms-RKSJ-V", "90ms-RKSJ-UCS2" );
136         cmapSubstitutions.put( "90msp-RKSJ-H", "90ms-RKSJ-UCS2" );
137         cmapSubstitutions.put( "90msp-RKSJ-V", "90ms-RKSJ-UCS2" );
138         cmapSubstitutions.put( "GBK-EUC-H", "GBK-EUC-UCS2" );
139         cmapSubstitutions.put( "GBK-EUC-V", "GBK-EUC-UCS2" );
140         cmapSubstitutions.put( "GBpc-EUC-H", "GBpc-EUC-UCS2C" );
141         cmapSubstitutions.put( "GBpc-EUC-V", "GBpc-EUC-UCS2C" );
142         
143         cmapSubstitutions.put( "UniJIS-UCS2-HW-H", "UniJIS-UCS2-H" );
144     }
145
146     /**
147      * This will clear AFM resources that are stored statically.
148      * This is usually not a problem unless you want to reclaim
149      * resources for a long running process.
150      *
151      * SPECIAL NOTE: The font calculations are currently in COSObject, which
152      * is where they will reside until PDFont is mature enough to take them over.
153      * PDFont is the appropriate place for them and not in COSObject but we need font
154      * calculations for text extractaion. THIS METHOD WILL BE MOVED OR REMOVED
155      * TO ANOTHER LOCATION IN A FUTURE VERSION OF PDFBOX.
156      */

157     public static void clearResources()
158     {
159         afmObjects.clear();
160         cmapObjects.clear();
161     }
162
163     /**
164      * Constructor.
165      */

166     public PDFont()
167     {
168         font = new COSDictionary();
169         font.setItem( COSName.TYPE, COSName.FONT );
170     }
171
172     /**
173      * Constructor.
174      *
175      * @param fontDictionary The font dictionary according to the PDF specification.
176      */

177     public PDFont( COSDictionary fontDictionary )
178     {
179         font = fontDictionary;
180     }
181
182     /**
183      * {@inheritDoc}
184      */

185     public COSBase getCOSObject()
186     {
187         return font;
188     }
189
190     /**
191      * This will get the font width for a character.
192      *
193      * @param c The character code to get the width for.
194      * @param offset The offset into the array.
195      * @param length The length of the data.
196      *
197      * @return The width is in 1000 unit of text space, ie 333 or 777
198      *
199      * @throws IOException If an error occurs while parsing.
200      */

201     public abstract float getFontWidth( byte[] c, int offset, int length ) throws IOException JavaDoc;
202     
203     /**
204      * This will get the font width for a character.
205      *
206      * @param c The character code to get the width for.
207      * @param offset The offset into the array.
208      * @param length The length of the data.
209      *
210      * @return The width is in 1000 unit of text space, ie 333 or 777
211      *
212      * @throws IOException If an error occurs while parsing.
213      */

214     public abstract float getFontHeight( byte[] c, int offset, int length ) throws IOException JavaDoc;
215
216     /**
217      * This will get the width of this string for this font.
218      *
219      * @param string The string to get the width of.
220      *
221      * @return The width of the string in 1000 units of text space, ie 333 567...
222      *
223      * @throws IOException If there is an error getting the width information.
224      */

225     public float getStringWidth( String JavaDoc string ) throws IOException JavaDoc
226     {
227         byte[] data = string.getBytes();
228         float totalWidth = 0;
229         for( int i=0; i<data.length; i++ )
230         {
231             totalWidth+=getFontWidth( data, i, 1 );
232         }
233         return totalWidth;
234     }
235
236     /**
237      * This will get the average font width for all characters.
238      *
239      * @return The width is in 1000 unit of text space, ie 333 or 777
240      *
241      * @throws IOException If an error occurs while parsing.
242      */

243     public abstract float getAverageFontWidth() throws IOException JavaDoc;
244
245     /**
246      * This will draw a string on a canvas using the font.
247      *
248      * @param string The string to draw.
249      * @param g The graphics to draw onto.
250      * @param fontSize The size of the font to draw.
251      * @param xScale The x scaling percent.
252      * @param yScale The y scaling percent.
253      * @param x The x coordinate to draw at.
254      * @param y The y coordinate to draw at.
255      *
256      * @throws IOException If there is an error drawing the specific string.
257      */

258     public abstract void drawString( String JavaDoc string, Graphics JavaDoc g, float fontSize,
259         float xScale, float yScale, float x, float y ) throws IOException JavaDoc;
260
261     /**
262      * Used for multibyte encodings.
263      *
264      * @param data The array of data.
265      * @param offset The offset into the array.
266      * @param length The number of bytes to use.
267      *
268      * @return The int value of data from the array.
269      */

270     protected int getCodeFromArray( byte[] data, int offset, int length )
271     {
272         int code = 0;
273         for( int i=0; i<length; i++ )
274         {
275             code <<= 8;
276             code |= (data[offset+i]+256)%256;
277         }
278         return code;
279     }
280
281     /**
282      * This will attempt to get the font width from an AFM file.
283      *
284      * @param code The character code we are trying to get.
285      *
286      * @return The font width from the AFM file.
287      *
288      * @throws IOException if we cannot find the width.
289      */

290     protected float getFontWidthFromAFMFile( int code ) throws IOException JavaDoc
291     {
292         float retval = 0;
293         FontMetric metric = getAFM();
294         if( metric != null )
295         {
296             Encoding encoding = getEncoding();
297             COSName characterName = encoding.getName( code );
298             retval = metric.getCharacterWidth( characterName.getName() );
299         }
300         return retval;
301     }
302
303     /**
304      * This will attempt to get the average font width from an AFM file.
305      *
306      * @return The average font width from the AFM file.
307      *
308      * @throws IOException if we cannot find the width.
309      */

310     protected float getAverageFontWidthFromAFMFile() throws IOException JavaDoc
311     {
312         float retval = 0;
313         FontMetric metric = getAFM();
314         if( metric != null )
315         {
316             retval = metric.getAverageCharacterWidth();
317         }
318         return retval;
319     }
320
321     /**
322      * This will get an AFM object if one exists.
323      *
324      * @return The afm object from the name.
325      *
326      * @throws IOException If there is an error getting the AFM object.
327      */

328     protected FontMetric getAFM() throws IOException JavaDoc
329     {
330         COSName name = (COSName)font.getDictionaryObject( COSName.BASE_FONT );
331         FontMetric result = null;
332         if( name != null )
333         {
334             result = (FontMetric)afmObjects.get( name );
335             if( result == null )
336             {
337                 String JavaDoc resource = (String JavaDoc)afmResources.get( name );
338                 if( resource == null )
339                 {
340                     //ok for now
341
//throw new IOException( "Unknown AFM font '" + name.getName() + "'" );
342
}
343                 else
344                 {
345                     InputStream JavaDoc afmStream = ResourceLoader.loadResource( resource );
346                     if( afmStream == null )
347                     {
348                         throw new IOException JavaDoc( "Can't handle font width:" + resource );
349                     }
350                     AFMParser parser = new AFMParser( afmStream );
351                     parser.parse();
352                     result = parser.getResult();
353                     afmObjects.put( name, result );
354                 }
355             }
356         }
357         return result;
358     }
359
360     /**
361      * This will perform the encoding of a character if needed.
362      *
363      * @param c The character to encode.
364      * @param offset The offset into the array to get the data
365      * @param length The number of bytes to read.
366      *
367      * @return The value of the encoded character.
368      *
369      * @throws IOException If there is an error during the encoding.
370      */

371     public String JavaDoc encode( byte[] c, int offset, int length ) throws IOException JavaDoc
372     {
373         String JavaDoc retval = null;
374         COSName fontSubtype = (COSName)font.getDictionaryObject( COSName.SUBTYPE );
375         String JavaDoc fontSubtypeName = fontSubtype.getName();
376         if( fontSubtypeName.equals( "Type0" ) ||
377             fontSubtypeName.equals( "Type1" ) ||
378             fontSubtypeName.equals( "TrueType" ))
379         {
380             if( cmap == null )
381             {
382                 if( font.getDictionaryObject( COSName.TO_UNICODE ) != null )
383                 {
384                     COSStream toUnicode = (COSStream)font.getDictionaryObject( COSName.TO_UNICODE );
385                     if( toUnicode != null )
386                     {
387                         parseCmap( toUnicode.getUnfilteredStream(), null );
388                     }
389                 }
390                 else
391                 {
392                     COSBase encoding = font.getDictionaryObject( COSName.ENCODING );
393                     if( encoding instanceof COSStream )
394                     {
395                         COSStream encodingStream = (COSStream)encoding;
396                         parseCmap( encodingStream.getUnfilteredStream(), null );
397                     }
398                     else if( fontSubtypeName.equals( "Type0" ) &&
399                              encoding instanceof COSName )
400                     {
401                         COSName encodingName = (COSName)encoding;
402                         cmap = (CMap)cmapObjects.get( encodingName );
403                         if( cmap != null )
404                         {
405                             cmap = (CMap)cmapObjects.get( encodingName );
406                         }
407                         else
408                         {
409                             String JavaDoc cmapName = encodingName.getName();
410                             cmapName = performCMAPSubstitution( cmapName );
411                             String JavaDoc resourceName = "Resources/cmap/" + cmapName;
412                             parseCmap( ResourceLoader.loadResource( resourceName ), encodingName );
413                             if( cmap == null && !encodingName.getName().equals( COSName.IDENTITY_H.getName() ) )
414                             {
415                                 throw new IOException JavaDoc( "Error: Could not find predefined " +
416                                 "CMAP file for '" + encodingName.getName() + "'" );
417                             }
418                         }
419                     }
420                     else if( encoding instanceof COSName ||
421                              encoding instanceof COSDictionary )
422                     {
423                         Encoding currentFontEncoding = getEncoding();
424                         if( currentFontEncoding != null )
425                         {
426                             retval = currentFontEncoding.getCharacter( getCodeFromArray( c, offset, length ) );
427                         }
428                     }
429                     else
430                     {
431                         COSDictionary fontDescriptor =
432                             (COSDictionary)font.getDictionaryObject( COSName.FONT_DESC );
433                         if( fontSubtypeName.equals( "TrueType" ) &&
434                             fontDescriptor != null &&
435                             (fontDescriptor.getDictionaryObject( COSName.FONT_FILE )!= null ||
436                              fontDescriptor.getDictionaryObject( COSName.FONT_FILE2 ) != null ||
437                              fontDescriptor.getDictionaryObject( COSName.FONT_FILE3 ) != null ) )
438                         {
439                             //If we are using an embedded font then there is not much we can do besides
440
//return the same character codes.
441
//retval = new String( c,offset, length );
442
retval = getStringFromArray( c, offset, length );
443                         }
444                         else
445                         {
446                             //this case will be handled below after checking the cmap
447
}
448                     }
449                 }
450
451
452             }
453         }
454         if( retval == null && cmap != null )
455         {
456             retval = cmap.lookup( c, offset, length );
457         }
458         //if we havn't found a value yet and
459
//we are still on the first byte and
460
//there is no cmap or the cmap does not have 2 byte mappings then try to encode
461
//using fallback methods.
462
if( retval == null &&
463             length == 1 &&
464             (cmap == null || !cmap.hasTwoByteMappings()))
465         {
466             Encoding encoding = getEncoding();
467             if( encoding != null )
468             {
469                 retval = encoding.getCharacter( getCodeFromArray( c, offset, length ) );
470             }
471             if( retval == null )
472             {
473                 retval = getStringFromArray( c, offset, length );
474             }
475         }
476         return retval;
477     }
478     
479     private static final String JavaDoc[] SINGLE_CHAR_STRING = new String JavaDoc[256];
480     private static final String JavaDoc[][] DOUBLE_CHAR_STRING = new String JavaDoc[256][256];
481     static
482     {
483         for( int i=0; i<256; i++ )
484         {
485             SINGLE_CHAR_STRING[i] = new String JavaDoc( new byte[] {(byte)i} );
486             for( int j=0; j<256; j++ )
487             {
488                 DOUBLE_CHAR_STRING[i][j] = new String JavaDoc( new byte[] {(byte)i, (byte)j} );
489             }
490         }
491     }
492     
493     private static String JavaDoc getStringFromArray( byte[] c, int offset, int length ) throws IOException JavaDoc
494     {
495         String JavaDoc retval = null;
496         if( length == 1 )
497         {
498             retval = SINGLE_CHAR_STRING[(c[offset]+256)%256];
499         }
500         else if( length == 2 )
501         {
502             retval = DOUBLE_CHAR_STRING[(c[offset]+256)%256][(c[offset+1]+256)%256];
503         }
504         else
505         {
506             throw new IOException JavaDoc( "Error:Unknown character length:" + length );
507         }
508         return retval;
509     }
510
511     /**
512      * Some cmap names are synonyms for other CMAPs. If that is the case
513      * then this method will perform that substitution.
514      *
515      * @param cmapName The name of the cmap to attempt to look up.
516      *
517      * @return Either the original name or the substituted name.
518      */

519     private String JavaDoc performCMAPSubstitution( String JavaDoc cmapName )
520     {
521         String JavaDoc retval = (String JavaDoc)cmapSubstitutions.get( cmapName );
522         if( retval == null )
523         {
524             //if there is no substitution then just return the same value.
525
retval = cmapName;
526         }
527         return retval;
528     }
529
530     private void parseCmap( InputStream JavaDoc cmapStream, COSName encodingName ) throws IOException JavaDoc
531     {
532         if( cmapStream != null )
533         {
534             CMapParser parser = new CMapParser();
535             cmap = parser.parse( cmapStream );
536             if( encodingName != null )
537             {
538                 cmapObjects.put( encodingName, cmap );
539             }
540         }
541     }
542     
543     /**
544      * The will set the encoding for this font.
545      *
546      * @param enc The font encoding.
547      */

548     public void setEncoding( Encoding enc )
549     {
550         font.setItem( COSName.ENCODING, enc );
551         fontEncoding = enc;
552     }
553
554     /**
555      * This will get or create the encoder.
556      *
557      * modified by Christophe Huault : DGBS Strasbourg huault@free.fr october 2004
558      *
559      * @return The encoding to use.
560      *
561      * @throws IOException If there is an error getting the encoding.
562      */

563     public Encoding getEncoding() throws IOException JavaDoc
564     {
565         EncodingManager manager = new EncodingManager();
566         if( fontEncoding == null )
567         {
568             COSBase encoding = font.getDictionaryObject( COSName.ENCODING );
569             if( encoding == null )
570             {
571                 FontMetric metric = getAFM();
572                 if( metric != null )
573                 {
574                     fontEncoding = new AFMEncoding( metric );
575                 }
576                 if( fontEncoding == null )
577                 {
578                     fontEncoding = manager.getStandardEncoding();
579                 }
580             }
581             /**
582              * Si la clé /Encoding existe dans le dictionnaire fonte il y a deux possibilités :
583              * 1er cas : elle est associé à une reference contenant un dictionnaire de type encoding.
584              * Ce dictionnaire PDF est représenté par un DictionaryEncoding.
585              * If the /Encoding Key does exist in the font dictionary, there are two cases :
586              * case one : The value associated with /Encoding is a reference to a dictionary.
587              * This dictionary is represented by an instance of DictionaryEncoding class
588              */

589             else if( encoding instanceof COSDictionary )
590             {
591                 COSDictionary encodingDic = (COSDictionary)encoding;
592                 //Let's see if the encoding dictionary has a base encoding
593
//If it does not then we will attempt to get it from the font
594
//file
595
COSName baseEncodingName = (COSName) encodingDic.getDictionaryObject(
596                     COSName.BASE_ENCODING);
597                 //on ajoute une entrée /BaseEncoding dans /Encoding uniquement si elle en est absente
598
//if not find in Encoding dictinary target, we try to find it from else where
599
if( baseEncodingName == null)
600                 {
601                     COSName fontEncodingFromFile = getEncodingFromFont();
602                     encodingDic.setItem(
603                         COSName.BASE_ENCODING,
604                         fontEncodingFromFile );
605                 }
606                 fontEncoding = new DictionaryEncoding( encodingDic );
607             }
608             else if( encoding instanceof COSName )
609             {
610                 if( !encoding.equals( COSName.IDENTITY_H ) )
611                 {
612                     fontEncoding = manager.getEncoding( (COSName)encoding );
613                 }
614             }
615             else
616             {
617                 throw new IOException JavaDoc( "Unexpected encoding type:" + encoding.getClass().getName() );
618             }
619         }
620         return fontEncoding;
621     }
622     
623     /**
624      * This will always return "Font" for fonts.
625      *
626      * @return The type of object that this is.
627      */

628     public String JavaDoc getType()
629     {
630         return font.getNameAsString( COSName.TYPE );
631     }
632     
633     /**
634      * This will get the subtype of font, Type1, Type3, ...
635      *
636      * @return The type of font that this is.
637      */

638     public String JavaDoc getSubType()
639     {
640         return font.getNameAsString( COSName.SUBTYPE );
641     }
642     
643     /**
644      * The PostScript name of the font.
645      *
646      * @return The postscript name of the font.
647      */

648     public String JavaDoc getBaseFont()
649     {
650         return font.getNameAsString( COSName.BASE_FONT );
651     }
652     
653     /**
654      * Set the PostScript name of the font.
655      *
656      * @param baseFont The postscript name for the font.
657      */

658     public void setBaseFont( String JavaDoc baseFont )
659     {
660         font.setName( COSName.BASE_FONT, baseFont );
661     }
662     
663     /**
664      * The code for the first char or -1 if there is none.
665      *
666      * @return The code for the first character.
667      */

668     public int getFirstChar()
669     {
670         return font.getInt( COSName.FIRST_CHAR, -1 );
671     }
672     
673     /**
674      * Set the first character this font supports.
675      *
676      * @param firstChar The first character.
677      */

678     public void setFirstChar( int firstChar )
679     {
680         font.setInt( COSName.FIRST_CHAR, firstChar );
681     }
682     
683     /**
684      * The code for the last char or -1 if there is none.
685      *
686      * @return The code for the last character.
687      */

688     public int getLastChar()
689     {
690         return font.getInt( COSName.LAST_CHAR, -1 );
691     }
692     
693     /**
694      * Set the last character this font supports.
695      *
696      * @param lastChar The last character.
697      */

698     public void setLastChar( int lastChar )
699     {
700         font.setInt( COSName.LAST_CHAR, lastChar );
701     }
702     
703     /**
704      * The widths of the characters. This will be null for the standard 14 fonts.
705      *
706      * @return The widths of the characters.
707      */

708     public List getWidths()
709     {
710         COSArray array = (COSArray)font.getDictionaryObject( COSName.WIDTHS );
711         return COSArrayList.convertFloatCOSArrayToList( array );
712     }
713     
714     /**
715      * Set the widths of the characters code.
716      *
717      * @param widths The widths of the character codes.
718      */

719     public void setWidths( List widths )
720     {
721         font.setItem( COSName.WIDTHS, COSArrayList.converterToCOSArray( widths ) );
722     }
723     
724     /**
725      * This will get the matrix that is used to transform glyph space to
726      * text space. By default there are 1000 glyph units to 1 text space
727      * unit, but type3 fonts can use any value.
728      *
729      * Note:If this is a type3 font then it can be modified via the PDType3Font.setFontMatrix, otherwise this
730      * is a read-only property.
731      *
732      * @return The matrix to transform from glyph space to text space.
733      */

734     public PDMatrix getFontMatrix()
735     {
736         PDMatrix matrix = null;
737         COSArray array = (COSArray)font.getDictionaryObject( COSName.FONT_MATRIX );
738         if( array == null )
739         {
740             array = new COSArray();
741             array.add( new COSFloat( 0.001f ) );
742             array.add( COSNumber.ZERO );
743             array.add( COSNumber.ZERO );
744             array.add( new COSFloat( 0.001f ) );
745             array.add( COSNumber.ZERO );
746             array.add( COSNumber.ZERO );
747         }
748         matrix = new PDMatrix(array);
749         
750         return matrix;
751     }
752     
753     /**
754      * Try to get the encoding for the font and add it to the target
755      * the target must be an an Encoding Dictionary.
756      *
757      * added by Christophe Huault : DGBS Strasbourg huault@free.fr october 2004
758      *
759      * @return The encoding from the font.
760      *
761      * @throws IOException If there is an error reading the file.
762      */

763     private COSName getEncodingFromFont() throws IOException JavaDoc
764     {
765         //This whole section of code needs to be replaced with an actual
766
//type1 font parser!!
767

768         
769         COSName retvalue = null;
770         //recuperer le programme de fonte dans son stream qui doit se trouver
771
//dans le flux référencé par à la clé FileFont lui même situé dans
772
//le dictionnaire associé à /FontDescriptor du dictionnaire de type /Font courrant
773
//get the font program in the stream which should be located in
774
//the /FileFont Stream object himself in the /FontDescriptior of the current
775
//font dictionary
776
COSDictionary fontDescriptor = (COSDictionary) font.getDictionaryObject(
777             COSName.FONT_DESC);
778         if( fontDescriptor != null )
779         {
780             COSStream fontFile = (COSStream) fontDescriptor.getDictionaryObject(
781                 COSName.FONT_FILE);
782             if( fontFile != null )
783             {
784                 BufferedReader JavaDoc in =
785                         new BufferedReader JavaDoc(new InputStreamReader JavaDoc(fontFile.getUnfilteredStream()));
786                 /**
787                  * this section parse the FileProgram stream searching for a /Encoding entry
788                  * the research stop if the entry "currentdict end" is reach or after 100 lignes
789                  */

790                 StringTokenizer JavaDoc st = null;
791                 boolean found = false;
792                 String JavaDoc line = "";
793                 String JavaDoc key = null;
794                 for( int i = 0; null!=( line = in.readLine() ) &&
795                                 i < 40 &&
796                                 !line.equals("currentdict end")
797                                 && !found; i++)
798                 {
799                     st = new StringTokenizer JavaDoc(line);
800                     if( st.hasMoreTokens() )
801                     {
802                         key = st.nextToken();
803                         if(key.equals("/Encoding") && st.hasMoreTokens() )
804                         {
805                             COSName value = COSName.getPDFName( st.nextToken() );
806                             found = true;
807                             if( value.equals( COSName.MAC_ROMAN_ENCODING ) ||
808                                 value.equals( COSName.PDF_DOC_ENCODING ) ||
809                                 value.equals( COSName.STANDARD_ENCODING ) ||
810                                 value.equals( COSName.WIN_ANSI_ENCODING ) )
811                             {
812                                 //value is expected to be one of the encodings
813
//ie. StandardEncoding,WinAnsiEncoding,MacRomanEncoding,PDFDocEncoding
814
retvalue = value;
815                             }
816                         }
817                     }
818                 }
819             }
820         }
821         return retvalue;
822     }
823     
824     /**
825      * This will get the fonts bouding box.
826      *
827      * @return The fonts bouding box.
828      *
829      * @throws IOException If there is an error getting the bounding box.
830      */

831     public abstract PDRectangle getFontBoundingBox() throws IOException JavaDoc;
832     
833     /**
834      * {@inheritDoc}
835      */

836     public boolean equals( Object JavaDoc other )
837     {
838         return other instanceof PDFont && ((PDFont)other).getCOSObject() == this.getCOSObject();
839     }
840     
841     /**
842      * {@inheritDoc}
843      */

844     public int hashCode()
845     {
846         return this.getCOSObject().hashCode();
847     }
848 }
Popular Tags