* Use this method instead of
*
* font.getDictionaryObject(COSName.ENCODING);
*
* @return
*/
private COSBase getEncodingObject(){
if(encodingObject==null){
encodingObject = font.getDictionaryObject( COSName.ENCODING );
}
return encodingObject;
}
/**
* This will perform the encoding of a character if needed.
*
* @param c The character to encode.
* @param offset The offset into the array to get the data
* @param length The number of bytes to read.
*
* @return The value of the encoded character.
*
* @throws IOException If there is an error during the encoding.
*/
public String encode( byte[] c, int offset, int length ) throws IOException
{
String retval = null;
COSName fontSubtype = (COSName)font.getDictionaryObject( COSName.SUBTYPE );
String fontSubtypeName = fontSubtype.getName();
if( fontSubtypeName.equals( "Type0" ) ||
fontSubtypeName.equals( "Type1" ) ||
fontSubtypeName.equals( "TrueType" ))
{
if( cmap == null )
{
if( font.getDictionaryObject( COSName.TO_UNICODE ) instanceof COSStream )
{
COSStream toUnicode = (COSStream)font.getDictionaryObject( COSName.TO_UNICODE );
if( toUnicode != null )
{
parseCmap( null, toUnicode.getUnfilteredStream(), null );
}
}
else
{
COSBase encoding = getEncodingObject(); //font.getDictionaryObject( COSName.ENCODING );
if( encoding instanceof COSStream )
{
COSStream encodingStream = (COSStream)encoding;
parseCmap( null, encodingStream.getUnfilteredStream(), null );
}
else if( fontSubtypeName.equals( "Type0" ) &&
encoding instanceof COSName )
{
COSName encodingName = (COSName)encoding;
cmap = (CMap)cmapObjects.get( encodingName );
if( cmap == null )
{
String cmapName = encodingName.getName();
String resourceRoot = "Resources/cmap/";
String resourceName = resourceRoot + cmapName;
parseCmap( resourceRoot, ResourceLoader.loadResource( resourceName ), encodingName );
if( cmap == null && !encodingName.getName().equals( COSName.IDENTITY_H.getName() ) )
{
throw new IOException( "Error: Could not find predefined " +
"CMAP file for '" + encodingName.getName() + "'" );
}
}
}
else if( encoding instanceof COSName ||
encoding instanceof COSDictionary )
{
Encoding currentFontEncoding = getEncoding();
if( currentFontEncoding != null )
{
retval = currentFontEncoding.getCharacter( getCodeFromArray( c, offset, length ) );
}
}
else
{
COSDictionary fontDescriptor =
(COSDictionary)font.getDictionaryObject( COSName.FONT_DESC );
if( fontSubtypeName.equals( "TrueType" ) &&
fontDescriptor != null &&
(fontDescriptor.getDictionaryObject( COSName.FONT_FILE )!= null ||
fontDescriptor.getDictionaryObject( COSName.FONT_FILE2 ) != null ||
fontDescriptor.getDictionaryObject( COSName.FONT_FILE3 ) != null ) )
{
//If we are using an embedded font then there is not much we can do besides
//return the same character codes.
//retval = new String( c,offset, length );
retval = getStringFromArray( c, offset, length );
}
else
{
//this case will be handled below after checking the cmap
}
}
}
}
}
if( retval == null && cmap != null )
{
retval = cmap.lookup( c, offset, length );
}
//if we havn't found a value yet and
//we are still on the first byte and
//there is no cmap or the cmap does not have 2 byte mappings then try to encode
//using fallback methods.
if( retval == null &&
length == 1 &&
(cmap == null || !cmap.hasTwoByteMappings()))
{
Encoding encoding = getEncoding();
if( encoding != null )
{
retval = encoding.getCharacter( getCodeFromArray( c, offset, length ) );
}
if( retval == null )
{
retval = getStringFromArray( c, offset, length );
}
}
return retval;
}
private static final String[] SINGLE_CHAR_STRING = new String[256];
private static final String[][] DOUBLE_CHAR_STRING = new String[256][256];
static
{
for( int i=0; i<256; i++ )
{
SINGLE_CHAR_STRING[i] = new String( new byte[] {(byte)i} );
for( int j=0; j<256; j++ )
{
DOUBLE_CHAR_STRING[i][j] = new String( new byte[] {(byte)i, (byte)j} );
}
}
}
private static String getStringFromArray( byte[] c, int offset, int length ) throws IOException
{
String retval = null;
if( length == 1 )
{
retval = SINGLE_CHAR_STRING[(c[offset]+256)%256];
}
else if( length == 2 )
{
retval = DOUBLE_CHAR_STRING[(c[offset]+256)%256][(c[offset+1]+256)%256];
}
else
{
throw new IOException( "Error:Unknown character length:" + length );
}
return retval;
}
private void parseCmap( String cmapRoot, InputStream cmapStream, COSName encodingName ) throws IOException
{
if( cmapStream != null )
{
CMapParser parser = new CMapParser();
cmap = parser.parse( cmapRoot, cmapStream );
if( encodingName != null )
{
cmapObjects.put( encodingName, cmap );
}
}
}
/**
* The will set the encoding for this font.
*
* @param enc The font encoding.
*/
public void setEncoding( Encoding enc )
{
font.setItem( COSName.ENCODING, enc );
fontEncoding = enc;
}
/**
* This will get or create the encoder.
*
* modified by Christophe Huault : DGBS Strasbourg huault@free.fr october 2004
*
* @return The encoding to use.
*
* @throws IOException If there is an error getting the encoding.
*/
public Encoding getEncoding() throws IOException
{
if( fontEncoding == null )
{
EncodingManager manager = getEncodingManager();
COSBase encoding = getEncodingObject(); //font.getDictionaryObject( COSName.ENCODING );
if( encoding == null )
{
FontMetric metric = getAFM();
if( metric != null )
{
fontEncoding = new AFMEncoding( metric );
}
if( fontEncoding == null )
{
fontEncoding = manager.getStandardEncoding();
}
}
/**
* Si la cl� /Encoding existe dans le dictionnaire fonte il y a deux possibilit�s :
* 1er cas : elle est associ� � une reference contenant un dictionnaire de type encoding.
* Ce dictionnaire PDF est repr�sent� par un DictionaryEncoding.
* If the /Encoding Key does exist in the font dictionary, there are two cases :
* case one : The value associated with /Encoding is a reference to a dictionary.
* This dictionary is represented by an instance of DictionaryEncoding class
*/
else if( encoding instanceof COSDictionary )
{
COSDictionary encodingDic = (COSDictionary)encoding;
//Let's see if the encoding dictionary has a base encoding
//If it does not then we will attempt to get it from the font
//file
COSName baseEncodingName = (COSName) encodingDic.getDictionaryObject(
COSName.BASE_ENCODING);
//on ajoute une entr�e /BaseEncoding dans /Encoding uniquement si elle en est absente
//if not find in Encoding dictinary target, we try to find it from else where
if( baseEncodingName == null)
{
COSName fontEncodingFromFile = getEncodingFromFont();
encodingDic.setItem(
COSName.BASE_ENCODING,
fontEncodingFromFile );
}
fontEncoding = new DictionaryEncoding( encodingDic );
}
else if( encoding instanceof COSName )
{
if( !encoding.equals( COSName.IDENTITY_H ) )
{
fontEncoding = manager.getEncoding( (COSName)encoding );
}
}
else
{
throw new IOException( "Unexpected encoding type:" + encoding.getClass().getName() );
}
}
return fontEncoding;
}
/**
* This will always return "Font" for fonts.
*
* @return The type of object that this is.
*/
public String getType()
{
return font.getNameAsString( COSName.TYPE );
}
/**
* This will get the subtype of font, Type1, Type3, ...
*
* @return The type of font that this is.
*/
public String getSubType()
{
return font.getNameAsString( COSName.SUBTYPE );
}
/**
* The PostScript name of the font.
*
* @return The postscript name of the font.
*/
public String getBaseFont()
{
return font.getNameAsString( COSName.BASE_FONT );
}
/**
* Set the PostScript name of the font.
*
* @param baseFont The postscript name for the font.
*/
public void setBaseFont( String baseFont )
{
font.setName( COSName.BASE_FONT, baseFont );
}
/**
* The code for the first char or -1 if there is none.
*
* @return The code for the first character.
*/
public int getFirstChar()
{
return font.getInt( COSName.FIRST_CHAR, -1 );
}
/**
* Set the first character this font supports.
*
* @param firstChar The first character.
*/
public void setFirstChar( int firstChar )
{
font.setInt( COSName.FIRST_CHAR, firstChar );
}
/**
* The code for the last char or -1 if there is none.
*
* @return The code for the last character.
*/
public int getLastChar()
{
return font.getInt( COSName.LAST_CHAR, -1 );
}
/**
* Set the last character this font supports.
*
* @param lastChar The last character.
*/
public void setLastChar( int lastChar )
{
font.setInt( COSName.LAST_CHAR, lastChar );
}
/**
* The widths of the characters. This will be null for the standard 14 fonts.
*
* @return The widths of the characters.
*/
public List getWidths()
{
COSArray array = (COSArray)font.getDictionaryObject( COSName.WIDTHS );
return COSArrayList.convertFloatCOSArrayToList( array );
}
/**
* Set the widths of the characters code.
*
* @param widths The widths of the character codes.
*/
public void setWidths( List widths )
{
font.setItem( COSName.WIDTHS, COSArrayList.converterToCOSArray( widths ) );
}
/**
* This will get the matrix that is used to transform glyph space to
* text space. By default there are 1000 glyph units to 1 text space
* unit, but type3 fonts can use any value.
*
* Note:If this is a type3 font then it can be modified via the PDType3Font.setFontMatrix, otherwise this
* is a read-only property.
*
* @return The matrix to transform from glyph space to text space.
*/
public PDMatrix getFontMatrix()
{
PDMatrix matrix = null;
COSArray array = (COSArray)font.getDictionaryObject( COSName.FONT_MATRIX );
if( array == null )
{
array = new COSArray();
array.add( new COSFloat( 0.001f ) );
array.add( COSNumber.ZERO );
array.add( COSNumber.ZERO );
array.add( new COSFloat( 0.001f ) );
array.add( COSNumber.ZERO );
array.add( COSNumber.ZERO );
}
matrix = new PDMatrix(array);
return matrix;
}
/**
* Try to get the encoding for the font and add it to the target
* the target must be an an Encoding Dictionary.
*
* added by Christophe Huault : DGBS Strasbourg huault@free.fr october 2004
*
* @return The encoding from the font.
*
* @throws IOException If there is an error reading the file.
*/
private COSName getEncodingFromFont() throws IOException
{
//This whole section of code needs to be replaced with an actual
//type1 font parser!!
COSName retvalue = null;
//recuperer le programme de fonte dans son stream qui doit se trouver
//dans le flux r�f�renc� par � la cl� FileFont lui m�me situ� dans
//le dictionnaire associ� � /FontDescriptor du dictionnaire de type /Font courrant
//get the font program in the stream which should be located in
//the /FileFont Stream object himself in the /FontDescriptior of the current
//font dictionary
COSDictionary fontDescriptor = (COSDictionary) font.getDictionaryObject(
COSName.FONT_DESC);
if( fontDescriptor != null )
{
COSStream fontFile = (COSStream) fontDescriptor.getDictionaryObject(
COSName.FONT_FILE);
if( fontFile != null )
{
BufferedReader in =
new BufferedReader(new InputStreamReader(fontFile.getUnfilteredStream()));
/**
* this section parse the FileProgram stream searching for a /Encoding entry
* the research stop if the entry "currentdict end" is reach or after 100 lignes
*/
StringTokenizer st = null;
boolean found = false;
String line = "";
String key = null;
for( int i = 0; null!=( line = in.readLine() ) &&
i < 40 &&
!line.equals("currentdict end")
&& !found; i++)
{
st = new StringTokenizer(line);
if( st.hasMoreTokens() )
{
key = st.nextToken();
if(key.equals("/Encoding") && st.hasMoreTokens() )
{
COSName value = COSName.getPDFName( st.nextToken() );
found = true;
if( value.equals( COSName.MAC_ROMAN_ENCODING ) ||
value.equals( COSName.PDF_DOC_ENCODING ) ||
value.equals( COSName.STANDARD_ENCODING ) ||
value.equals( COSName.WIN_ANSI_ENCODING ) )
{
//value is expected to be one of the encodings
//ie. StandardEncoding,WinAnsiEncoding,MacRomanEncoding,PDFDocEncoding
retvalue = value;
}
}
}
}
}
}
return retvalue;
}
/**
* This will get the fonts bouding box.
*
* @return The fonts bouding box.
*
* @throws IOException If there is an error getting the bounding box.
*/
public abstract PDRectangle getFontBoundingBox() throws IOException;
/**
* {@inheritDoc}
*/
public boolean equals( Object other )
{
return other instanceof PDFont && ((PDFont)other).getCOSObject() == this.getCOSObject();
}
/**
* {@inheritDoc}
*/
public int hashCode()
{
return this.getCOSObject().hashCode();
}
private static EncodingManager encodingManager = null;
protected static EncodingManager getEncodingManager(){
if(encodingManager == null){
encodingManager = new EncodingManager();
}
return encodingManager;
}
}