org.apache.pdfbox.pdmodel.font.PDType1Font Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pdfbox Show documentation
Show all versions of pdfbox Show documentation
The Apache PDFBox library is an open source Java tool for working with PDF documents.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pdfbox.pdmodel.font;
import java.awt.Font;
import java.awt.FontFormatException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fontbox.afm.FontMetric;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSFloat;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.encoding.AFMEncoding;
import org.apache.pdfbox.encoding.Encoding;
import org.apache.pdfbox.encoding.EncodingManager;
import org.apache.pdfbox.encoding.Type1Encoding;
import org.apache.pdfbox.encoding.WinAnsiEncoding;
import org.apache.pdfbox.pdmodel.common.PDMatrix;
import org.apache.pdfbox.pdmodel.common.PDStream;
/**
* This is implementation of the Type1 Font.
*
* @author Ben Litchfield
* @version $Revision: 1.11 $
*/
public class PDType1Font extends PDSimpleFont
{
/**
* Log instance.
*/
private static final Log log = LogFactory.getLog(PDType1Font.class);
private PDType1CFont type1CFont = null;
/**
* Standard Base 14 Font.
*/
public static final PDType1Font TIMES_ROMAN = new PDType1Font( "Times-Roman" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font TIMES_BOLD = new PDType1Font( "Times-Bold" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font TIMES_ITALIC = new PDType1Font( "Times-Italic" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font TIMES_BOLD_ITALIC = new PDType1Font( "Times-BoldItalic" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font HELVETICA = new PDType1Font( "Helvetica" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font HELVETICA_BOLD = new PDType1Font( "Helvetica-Bold" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font HELVETICA_OBLIQUE = new PDType1Font( "Helvetica-Oblique" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font HELVETICA_BOLD_OBLIQUE = new PDType1Font( "Helvetica-BoldOblique" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font COURIER = new PDType1Font( "Courier" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font COURIER_BOLD = new PDType1Font( "Courier-Bold" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font COURIER_OBLIQUE = new PDType1Font( "Courier-Oblique" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font COURIER_BOLD_OBLIQUE = new PDType1Font( "Courier-BoldOblique" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font SYMBOL = new PDType1Font( "Symbol" );
/**
* Standard Base 14 Font.
*/
public static final PDType1Font ZAPF_DINGBATS = new PDType1Font( "ZapfDingbats" );
private static final Map STANDARD_14 = new HashMap();
static
{
STANDARD_14.put( TIMES_ROMAN.getBaseFont(), TIMES_ROMAN );
STANDARD_14.put( TIMES_BOLD.getBaseFont(), TIMES_BOLD );
STANDARD_14.put( TIMES_ITALIC.getBaseFont(), TIMES_ITALIC );
STANDARD_14.put( TIMES_BOLD_ITALIC.getBaseFont(), TIMES_BOLD_ITALIC );
STANDARD_14.put( HELVETICA.getBaseFont(), HELVETICA );
STANDARD_14.put( HELVETICA_BOLD.getBaseFont(), HELVETICA_BOLD );
STANDARD_14.put( HELVETICA_OBLIQUE.getBaseFont(), HELVETICA_OBLIQUE );
STANDARD_14.put( HELVETICA_BOLD_OBLIQUE.getBaseFont(), HELVETICA_BOLD_OBLIQUE );
STANDARD_14.put( COURIER.getBaseFont(), COURIER );
STANDARD_14.put( COURIER_BOLD.getBaseFont(), COURIER_BOLD );
STANDARD_14.put( COURIER_OBLIQUE.getBaseFont(), COURIER_OBLIQUE );
STANDARD_14.put( COURIER_BOLD_OBLIQUE.getBaseFont(), COURIER_BOLD_OBLIQUE );
STANDARD_14.put( SYMBOL.getBaseFont(), SYMBOL );
STANDARD_14.put( ZAPF_DINGBATS.getBaseFont(), ZAPF_DINGBATS );
}
private Font awtFont = null;
/**
* Constructor.
*/
public PDType1Font()
{
super();
font.setItem( COSName.SUBTYPE, COSName.TYPE1 );
}
/**
* Constructor.
*
* @param fontDictionary The font dictionary according to the PDF specification.
*/
public PDType1Font( COSDictionary fontDictionary )
{
super( fontDictionary );
PDFontDescriptor fd = getFontDescriptor();
if (fd != null && fd instanceof PDFontDescriptorDictionary)
{
// a Type1 font may contain a Type1C font
PDStream fontFile3 = ((PDFontDescriptorDictionary)fd).getFontFile3();
if (fontFile3 != null)
{
try
{
type1CFont = new PDType1CFont( super.font );
}
catch (IOException exception)
{
log.info("Can't read the embedded type1C font " + fd.getFontName() );
}
}
}
}
/**
* Constructor.
*
* @param baseFont The base font for this font.
*/
public PDType1Font( String baseFont )
{
this();
setBaseFont( baseFont );
setFontEncoding(new WinAnsiEncoding());
setEncoding(COSName.WIN_ANSI_ENCODING);
}
/**
* A convenience method to get one of the standard 14 font from name.
*
* @param name The name of the font to get.
*
* @return The font that matches the name or null if it does not exist.
*/
public static PDType1Font getStandardFont( String name )
{
return (PDType1Font)STANDARD_14.get( name );
}
/**
* This will get the names of the standard 14 fonts.
*
* @return An array of the names of the standard 14 fonts.
*/
public static String[] getStandard14Names()
{
return (String[])STANDARD_14.keySet().toArray( new String[14] );
}
/**
* {@inheritDoc}
*/
public Font getawtFont() throws IOException
{
if( awtFont == null )
{
if (type1CFont != null)
{
awtFont = type1CFont.getawtFont();
}
else
{
String baseFont = getBaseFont();
PDFontDescriptor fd = getFontDescriptor();
if (fd != null && fd instanceof PDFontDescriptorDictionary)
{
PDFontDescriptorDictionary fdDictionary = (PDFontDescriptorDictionary)fd;
if( fdDictionary.getFontFile() != null )
{
try
{
// create a type1 font with the embedded data
awtFont = Font.createFont( Font.TYPE1_FONT, fdDictionary.getFontFile().createInputStream() );
}
catch (FontFormatException e)
{
log.info("Can't read the embedded type1 font " + fd.getFontName() );
}
}
if (awtFont == null)
{
// check if the font is part of our environment
if (fd.getFontName() != null)
{
awtFont = FontManager.getAwtFont(fd.getFontName());
}
if (awtFont == null)
{
log.info("Can't find the specified font " + fd.getFontName() );
}
}
}
else
{
// check if the font is part of our environment
awtFont = FontManager.getAwtFont(baseFont);
if (awtFont == null)
{
log.info("Can't find the specified basefont " + baseFont );
}
}
}
if (awtFont == null)
{
// we can't find anything, so we have to use the standard font
awtFont = FontManager.getStandardFont();
log.info("Using font "+awtFont.getName()+ " instead");
}
}
return awtFont;
}
protected void determineEncoding()
{
super.determineEncoding();
Encoding fontEncoding = getFontEncoding();
if(fontEncoding == null)
{
FontMetric metric = getAFM();
if (metric != null)
{
fontEncoding = new AFMEncoding( metric );
}
setFontEncoding(fontEncoding);
}
getEncodingFromFont(getFontEncoding() == null);
}
/**
* Tries to get the encoding for the type1 font.
*
*/
private void getEncodingFromFont(boolean extractEncoding)
{
// This whole section of code needs to be replaced with an actual type1 font parser!!
// Get the font program from the embedded type font.
PDFontDescriptor fontDescriptor = getFontDescriptor();
if( fontDescriptor != null && fontDescriptor instanceof PDFontDescriptorDictionary)
{
PDStream fontFile = ((PDFontDescriptorDictionary)fontDescriptor).getFontFile();
if( fontFile != null )
{
BufferedReader in = null;
try
{
in = new BufferedReader(new InputStreamReader(fontFile.createInputStream(), "ISO-8859-1"));
// this section parses the font program stream searching for a /Encoding entry
// if it contains an array of values a Type1Encoding will be returned
// if it encoding contains an encoding name the corresponding Encoding will be returned
String line = "";
Type1Encoding encoding = null;
while( (line = in.readLine()) != null)
{
if (extractEncoding)
{
if (line.startsWith("currentdict end")) {
if (encoding != null)
setFontEncoding(encoding);
break;
}
if (line.startsWith("/Encoding"))
{
if(line.contains("array"))
{
StringTokenizer st = new StringTokenizer(line);
// ignore the first token
st.nextElement();
int arraySize = Integer.parseInt(st.nextToken());
encoding = new Type1Encoding(arraySize);
}
// if there is already an encoding, we don't need to
// assign another one
else if (getFontEncoding() == null)
{
StringTokenizer st = new StringTokenizer(line);
// ignore the first token
st.nextElement();
String type1Encoding = st.nextToken();
setFontEncoding(
EncodingManager.INSTANCE.getEncoding(
COSName.getPDFName(type1Encoding)));
break;
}
}
else if (line.startsWith("dup")) {
StringTokenizer st = new StringTokenizer(line.replaceAll("/"," /"));
// ignore the first token
st.nextElement();
try
{
int index = Integer.parseInt(st.nextToken());
String name = st.nextToken();
if(encoding == null)
{
log.warn("Unable to get character encoding. " +
"Encoding definition found without /Encoding line.");
}
else
{
encoding.addCharacterEncoding(index, name.replace("/", ""));
}
}
catch(NumberFormatException exception)
{
// there are (tex?)-some fonts containing postscript code like the following,
// which has to be ignored, see PDFBOX-1481
// dup dup 161 10 getinterval 0 exch putinterval ....
log.debug("Malformed encoding definition ignored (line="+line+")");
}
continue;
}
}
// according to the pdf reference, all font matrices should be same, except for type 3 fonts.
// but obviously there are some type1 fonts with different matrix values, see pdf sample
// attached to PDFBOX-935
if (line.startsWith("/FontMatrix"))
{
// most likely all matrix values are in the same line than the keyword
if (line.indexOf("[") > -1)
{
String matrixValues = line.substring(line.indexOf("[")+1,line.lastIndexOf("]"));
StringTokenizer st = new StringTokenizer(matrixValues);
COSArray array = new COSArray();
if (st.countTokens() >= 6)
{
try
{
for (int i=0;i<6;i++)
{
COSFloat floatValue = new COSFloat(Float.parseFloat(st.nextToken()));
array.add(floatValue);
}
}
catch (NumberFormatException exception)
{
log.error("Can't read the fontmatrix from embedded font file!");
}
fontMatrix = new PDMatrix(array);
}
}
else
{
// there are fonts where all values are on a separate line, see PDFBOX-1611
COSArray array = new COSArray();
while((line = in.readLine()) != null)
{
if (line.startsWith("["))
{
continue;
}
if (line.endsWith("]"))
{
break;
}
try
{
COSFloat floatValue = new COSFloat(Float.parseFloat(line));
array.add(floatValue);
}
catch (NumberFormatException exception)
{
log.error("Can't read the fontmatrix from embedded font file!");
}
}
if (array.size() == 6)
{
fontMatrix = new PDMatrix(array);
}
else
{
log.error("Can't read the fontmatrix from embedded font file, not enough values!");
}
}
}
}
}
catch(IOException exception)
{
log.error("Error: Could not extract the encoding from the embedded type1 font.");
}
finally
{
if (in != null)
{
try
{
in.close();
}
catch(IOException exception)
{
log.error("An error occurs while closing the stream used to read the embedded type1 font.");
}
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String encode(byte[] c, int offset, int length) throws IOException
{
if (type1CFont != null && getFontEncoding() == null)
{
String character = type1CFont.encode(c, offset, length);
if (character != null)
{
return character;
}
}
return super.encode(c, offset, length);
}
/**
* {@inheritDoc}
*/
@Override
public int encodeToCID( byte[] c, int offset, int length ) throws IOException
{
if (type1CFont != null && getFontEncoding() == null)
{
return type1CFont.encodeToCID(c, offset, length);
}
else
{
return super.encodeToCID(c, offset, length);
}
}
/**
* {@inheritDoc}
*/
@Override
public PDMatrix getFontMatrix()
{
if (type1CFont != null)
{
return type1CFont.getFontMatrix();
}
else
{
return super.getFontMatrix();
}
}
@Override
public void clear()
{
super.clear();
if (type1CFont != null)
{
type1CFont.clear();
type1CFont = null;
}
}
}