package com.tom_roush.pdfbox.pdmodel.font; import android.graphics.Path; import android.util.Log; import com.tom_roush.fontbox.FontBoxFont; import com.tom_roush.fontbox.util.BoundingBox; import com.tom_roush.pdfbox.cos.COSArray; import com.tom_roush.pdfbox.cos.COSDictionary; import com.tom_roush.pdfbox.cos.COSName; import com.tom_roush.pdfbox.cos.COSStream; import com.tom_roush.pdfbox.pdmodel.PDResources; import com.tom_roush.pdfbox.pdmodel.common.PDRectangle; import com.tom_roush.pdfbox.pdmodel.font.encoding.DictionaryEncoding; import com.tom_roush.pdfbox.pdmodel.font.encoding.Encoding; import com.tom_roush.pdfbox.pdmodel.font.encoding.GlyphList; import com.tom_roush.pdfbox.util.Matrix; import com.tom_roush.pdfbox.util.Vector; import java.io.IOException; import java.io.InputStream; /** * A PostScript Type 3 Font. * * @author Ben Litchfield */ public class PDType3Font extends PDSimpleFont { private PDResources resources; private COSDictionary charProcs; private Matrix fontMatrix; /** * Constructor. * * @param fontDictionary The font dictionary according to the PDF specification. */ public PDType3Font(COSDictionary fontDictionary) throws IOException { super(fontDictionary); readEncoding(); } @Override public String getName() { return dict.getNameAsString(COSName.NAME); } @Override protected final void readEncoding() throws IOException { COSDictionary encodingDict = (COSDictionary)dict.getDictionaryObject(COSName.ENCODING); encoding = new DictionaryEncoding(encodingDict); glyphList = GlyphList.getZapfDingbats(); } @Override protected Encoding readEncodingFromFont() throws IOException { // Type 3 fonts do not have a built-in encoding throw new UnsupportedOperationException("not supported for Type 3 fonts"); } @Override protected Boolean isFontSymbolic() { return false; } @Override public Path getPath(String name) throws IOException { // Type 3 fonts do not use vector paths throw new UnsupportedOperationException("not supported for Type 3 fonts"); } @Override public boolean hasGlyph(String name) throws IOException { COSStream stream = (COSStream) getCharProcs().getDictionaryObject(COSName.getPDFName(name)); return stream != null; } @Override public FontBoxFont getFontBoxFont() { // Type 3 fonts do not use FontBox fonts throw new UnsupportedOperationException("not supported for Type 3 fonts"); } @Override public Vector getDisplacement(int code) throws IOException { return getFontMatrix().transform(new Vector(getWidth(code), 0)); } @Override public float getWidth(int code) throws IOException { int firstChar = dict.getInt(COSName.FIRST_CHAR, -1); int lastChar = dict.getInt(COSName.LAST_CHAR, -1); if (getWidths().size() > 0 && code >= firstChar && code <= lastChar) { return getWidths().get(code - firstChar).floatValue(); } else { PDFontDescriptor fd = getFontDescriptor(); if (fd != null) { return fd.getMissingWidth(); } else { // todo: call getWidthFromFont? Log.e("PdfBox-Android", "No width for glyph " + code + " in font " + getName()); return 0; } } } @Override public float getWidthFromFont(int code) { // todo: could these be extracted from the font's stream? throw new UnsupportedOperationException("not suppported"); } @Override public boolean isEmbedded() { return true; } @Override public float getHeight(int code) throws IOException { PDFontDescriptor desc = getFontDescriptor(); if (desc != null) { // the following values are all more or less accurate at least all are average // values. Maybe we'll find another way to get those value for every single glyph // in the future if needed PDRectangle fontBBox = desc.getFontBoundingBox(); float retval = 0; if (fontBBox != null) { retval = fontBBox.getHeight() / 2; } if (retval == 0) { retval = desc.getCapHeight(); } if (retval == 0) { retval = desc.getAscent(); } if (retval == 0) { retval = desc.getXHeight(); if (retval > 0) { retval -= desc.getDescent(); } } return retval; } return 0; } @Override protected byte[] encode(int unicode) throws IOException { throw new UnsupportedOperationException("Not implemented: Type3"); } @Override public int readCode(InputStream in) throws IOException { return in.read(); } @Override public Matrix getFontMatrix() { if (fontMatrix == null) { COSArray array = (COSArray) dict.getDictionaryObject(COSName.FONT_MATRIX); if (array != null) { fontMatrix = new Matrix(array); } else { return super.getFontMatrix(); } } return fontMatrix; } @Override public boolean isDamaged() { // there's no font file to load return false; } /** * Returns the optional resources of the type3 stream. * * @return the resources bound to be used when parsing the type3 stream */ public PDResources getResources() { if (resources == null) { COSDictionary resources = (COSDictionary) dict.getDictionaryObject(COSName.RESOURCES); if (resources != null) { this.resources = new PDResources(resources); } } return resources; } /** * This will get the fonts bounding box. * * @return The fonts bounding box. */ public PDRectangle getFontBBox() { COSArray rect = (COSArray) dict.getDictionaryObject(COSName.FONT_BBOX); PDRectangle retval = null; if(rect != null) { retval = new PDRectangle(rect); } return retval; } @Override public BoundingBox getBoundingBox() { PDRectangle rect = getFontBBox(); return new BoundingBox(rect.getLowerLeftX(), rect.getLowerLeftY(), rect.getWidth(), rect.getHeight()); } /** * Returns the dictionary containing all streams to be used to render the glyphs. * * @return the dictionary containing all glyph streams. */ public COSDictionary getCharProcs() { if (charProcs == null) { charProcs = (COSDictionary) dict.getDictionaryObject(COSName.CHAR_PROCS); } return charProcs; } /** * Returns the stream of the glyph for the given character code * * @param code character code * @return the stream to be used to render the glyph */ public PDType3CharProc getCharProc(int code) { String name = getEncoding().getName(code); if (name != null) { COSStream stream; stream = (COSStream)getCharProcs().getDictionaryObject(COSName.getPDFName(name)); return new PDType3CharProc(this, stream); } return null; } }