package com.tom_roush.pdfbox.pdmodel.font;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.Log;
import com.tom_roush.fontbox.EncodedFont;
import com.tom_roush.fontbox.FontBoxFont;
import com.tom_roush.fontbox.cff.CFFParser;
import com.tom_roush.fontbox.cff.CFFType1Font;
import com.tom_roush.fontbox.util.BoundingBox;
import com.tom_roush.pdfbox.cos.COSDictionary;
import com.tom_roush.pdfbox.cos.COSName;
import com.tom_roush.pdfbox.io.IOUtils;
import com.tom_roush.pdfbox.pdmodel.common.PDStream;
import com.tom_roush.pdfbox.pdmodel.font.encoding.Encoding;
import com.tom_roush.pdfbox.pdmodel.font.encoding.StandardEncoding;
import com.tom_roush.pdfbox.pdmodel.font.encoding.Type1Encoding;
import com.tom_roush.pdfbox.util.Matrix;
import com.tom_roush.pdfbox.util.awt.AffineTransform;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Type 1-equivalent CFF font.
*
* @author Villu Ruusmann
* @author John Hewson
*/
public class PDType1CFont extends PDSimpleFont
{
private final Map<String, Float> glyphHeights = new HashMap<String, Float>();
private Float avgWidth = null;
private Matrix fontMatrix;
private final AffineTransform fontMatrixTransform;
private final CFFType1Font cffFont; // embedded font
private final FontBoxFont genericFont; // embedded or system font for rendering
private final boolean isEmbedded;
private final boolean isDamaged;
/**
* Constructor.
*
* @param fontDictionary the corresponding dictionary
* @throws IOException it something went wrong
*/
public PDType1CFont(COSDictionary fontDictionary) throws IOException
{
super(fontDictionary);
PDFontDescriptor fd = getFontDescriptor();
byte[] bytes = null;
if (fd != null)
{
PDStream ff3Stream = fd.getFontFile3();
if (ff3Stream != null)
{
bytes = IOUtils.toByteArray(ff3Stream.createInputStream());
if (bytes.length == 0)
{
Log.e("PdfBox-Android", "Invalid data for embedded Type1C font " + getName());
bytes = null;
}
}
}
boolean fontIsDamaged = false;
CFFType1Font cffEmbedded = null;
try
{
if (bytes != null)
{
// note: this could be an OpenType file, fortunately CFFParser can handle that
CFFParser cffParser = new CFFParser();
cffEmbedded = (CFFType1Font)cffParser.parse(bytes).get(0);
}
}
catch (IOException e)
{
Log.e("PdfBox-Android", "Can't read the embedded Type1C font " + getName(), e);
fontIsDamaged = true;
}
isDamaged = fontIsDamaged;
cffFont = cffEmbedded;
if (cffFont != null)
{
genericFont = cffFont;
isEmbedded = true;
}
else
{
FontMapping<FontBoxFont> mapping = FontMapper.getFontBoxFont(getBaseFont(), fd);
genericFont = mapping.getFont();
if (mapping.isFallback())
{
Log.w("PdfBox-Android",
"Using fallback font " + genericFont.getName() + " for " + getBaseFont());
}
isEmbedded = false;
}
readEncoding();
fontMatrixTransform = getFontMatrix().createAffineTransform();
fontMatrixTransform.scale(1000, 1000);
}
@Override
public FontBoxFont getFontBoxFont()
{
return genericFont;
}
/**
* Returns the PostScript name of the font.
*/
public final String getBaseFont()
{
return dict.getNameAsString(COSName.BASE_FONT);
}
@Override
public Path getPath(String name) throws IOException
{
// Acrobat only draws .notdef for embedded or "Standard 14" fonts, see PDFBOX-2372
if (isEmbedded() && name.equals(".notdef") && !isEmbedded() && !isStandard14())
{
return new Path();
}
else
{
return genericFont.getPath(name);
}
}
@Override
public boolean hasGlyph(String name) throws IOException
{
return genericFont.hasGlyph(name);
}
@Override
public final String getName()
{
return getBaseFont();
}
@Override
public BoundingBox getBoundingBox() throws IOException
{
return genericFont.getFontBBox();
}
//@Override
public String codeToName(int code)
{
return getEncoding().getName(code);
}
@Override
protected Encoding readEncodingFromFont() throws IOException
{
if (getStandard14AFM() != null)
{
// read from AFM
return new Type1Encoding(getStandard14AFM());
}
else
{
// extract from Type1 font/substitute
if (genericFont instanceof EncodedFont)
{
//FIXME dead instanceof
return Type1Encoding.fromFontBox(((EncodedFont) genericFont).getEncoding());
}
else
{
// default (only happens with TTFs)
return StandardEncoding.INSTANCE;
}
}
}
@Override
public int readCode(InputStream in) throws IOException
{
return in.read();
}
@Override
public final Matrix getFontMatrix()
{
if (fontMatrix == null)
{
List<Number> numbers = null;
try
{
numbers = genericFont.getFontMatrix();
}
catch (IOException e)
{
fontMatrix = DEFAULT_FONT_MATRIX;
}
if (numbers != null && numbers.size() == 6)
{
fontMatrix = new Matrix(
numbers.get(0).floatValue(), numbers.get(1).floatValue(),
numbers.get(2).floatValue(), numbers.get(3).floatValue(),
numbers.get(4).floatValue(), numbers.get(5).floatValue());
}
else
{
return super.getFontMatrix();
}
}
return fontMatrix;
}
@Override
public boolean isDamaged()
{
return isDamaged;
}
@Override
public float getWidthFromFont(int code) throws IOException
{
String name = codeToName(code);
float width = genericFont.getWidth(name);
PointF p = new PointF(width, 0f);
fontMatrixTransform.transform(p, p);
return p.x;
}
@Override
public boolean isEmbedded()
{
return isEmbedded;
}
@Override
public float getHeight(int code) throws IOException
{
String name = codeToName(code);
float height = 0;
if (!glyphHeights.containsKey(name))
{
height = cffFont.getType1CharString(name).getBounds().height(); // todo: cffFont could be null
glyphHeights.put(name, height);
}
return height;
}
@Override
protected byte[] encode(int unicode) throws IOException
{
throw new UnsupportedOperationException("Not implemented: Type1C");
}
@Override
public float getStringWidth(String string) throws IOException
{
float width = 0;
for (int i = 0; i < string.length(); i++)
{
int codePoint = string.codePointAt(i);
String name = getGlyphList().codePointToName(codePoint);
width += cffFont.getType1CharString(name).getWidth();
}
return width;
}
@Override
public float getAverageFontWidth()
{
if (avgWidth == null)
{
avgWidth = getAverageCharacterWidth();
}
return avgWidth;
}
/**
* Returns the embedded Type 1-equivalent CFF font.
*
* @return the cffFont
*/
public CFFType1Font getCFFType1Font()
{
return cffFont;
}
// todo: this is a replacement for FontMetrics method
private float getAverageCharacterWidth()
{
// todo: not implemented, highly suspect
return 500;
}
}