/* * Copyright (c) 2013 Allogy Interactive. * * Licensed 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 com.hsl.txtreader; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.util.Log; import com.sun.pdfview.PDFCMap; import com.sun.pdfview.PDFFontDescriptor; import com.sun.pdfview.PDFGlyph; import com.sun.pdfview.PDFObject; /** * a Font definition for PDF files * @author Mike Wessler */ public abstract class PDFFont { /** the font SubType of this font */ private String subtype; /** the postscript name of this font */ private String baseFont; /** the font encoding (maps character ids to glyphs) */ private PDFFontEncoding encoding; /** the font descriptor */ private PDFFontDescriptor descriptor; /** the CMap that maps this font to unicode values */ private PDFCMap unicodeMap; /** a cache of glyphs indexed by character */ private Map<Character,PDFGlyph> charCache; /** * get the PDFFont corresponding to the font described in a PDFObject. * The object is actually a dictionary containing the following keys:<br> * Type = "Font"<br> * Subtype = (Type1 | TrueType | Type3 | Type0 | MMType1 | CIDFontType0 | * CIDFontType2)<br> * FirstChar = #<br> * LastChar = #<br> * Widths = array of #<br> * Encoding = (some name representing a dictionary in the resources | an * inline dictionary) * <p> * For Type1 and TrueType fonts, the dictionary also contains:<br> * BaseFont = (some name, or XXXXXX+Name as a subset of font Name) * <p> * For Type3 font, the dictionary contains:<br> * FontBBox = (rectangle)<br> * FontMatrix = (array, typically [0.001, 0, 0, 0.001, 0, 0])<br> * CharProcs = (dictionary) * Resources = (dictionary) */ public synchronized static PDFFont getFont(PDFObject obj, HashMap<String,PDFObject> resources) throws IOException { // the obj is actually a dictionary containing: // Type (=Font) // Subtype (Type1, TrueType, Type3, Type0, MMType1, CIDFontType0,2) // FirstChar (int) // LastChar (int) // Widths (array) // Encoding (name or dict) : assumes StandardEncoding // and........ // Type1 and TrueType fonts: // BaseFont (name) // may be XXXXXX+Fontname as a subset. // FontDescriptor (dict) // Type3 fonts: // FontBBox (rectangle) // FontMatrix (array) // e.g. [0.001 0 0 0.001 0 0] // CharProcs (dict) // Resources (dict) // // Font descriptor (Type1 and TrueType fonts): // FontName (name) // Flags (1=monospace, 2=serif, 4=script, 7=italic, 19=bold) // FontBBox (rectangle) // ItalicAngle (float) // Ascent (float) // Descent (float) // CapHeight (float) // StemV (float) // FontFile (stream for Type1 fonts) // FontFile2 (stream for TrueType fonts) // FontFile3 (stream for CFF/Type1C fonts) // // Font data can be Type1, TrueType(native), or Type1C PDFFont font = (PDFFont) obj.getCache(); if (font != null) { return font; } String baseFont = null; PDFFontEncoding encoding = null; PDFFontDescriptor descriptor = null; String subType = obj.getDictRef("Subtype").getStringValue(); if (subType == null) { subType = obj.getDictRef("S").getStringValue(); } PDFObject baseFontObj = obj.getDictRef("BaseFont"); PDFObject encodingObj = obj.getDictRef("Encoding"); PDFCMap toUnicodeM = null; PDFObject toUnicodeObj = obj.getDictRef("ToUnicode"); if (toUnicodeObj != null) { //Log.i("PDFFont", toUnicodeObj.toString()); toUnicodeM = new PDFToUnicodeCMap(toUnicodeObj); } if (baseFontObj != null) { baseFont = baseFontObj.getStringValue(); } else { baseFontObj = obj.getDictRef("Name"); if (baseFontObj != null) { baseFont = baseFontObj.getStringValue(); } } if (encodingObj != null) { encoding = new PDFFontEncoding(subType, encodingObj); } font = new DummyFont(baseFont, obj, descriptor); /* if (descObj != null) { descriptor = new PDFFontDescriptor(descObj); } else { descriptor = new PDFFontDescriptor(baseFont); } if (subType.equals("Type0")) { font = new Type0Font(baseFont, obj, descriptor); } else if (subType.equals("Type1")) { // load a type1 font if (descriptor == null) { // it's one of the built-in fonts font = new BuiltinFont(baseFont, obj); } else if (descriptor.getFontFile() != null) { // it's a Type1 font, included. font = new Type1Font(baseFont, obj, descriptor); } else if (descriptor.getFontFile3() != null) { // it's a CFF (Type1C) font font = new Type1CFont(baseFont, obj, descriptor); } else { // no font info. Fake it based on the FontDescriptor // System.out.println("Fakeout native font"); font = new BuiltinFont(baseFont, obj, descriptor); } } else if (subType.equals("TrueType")) { if (descriptor.getFontFile2() != null) { // load a TrueType font font = new TTFFont(baseFont, obj, descriptor); } else { // fake it with a built-in font font = new BuiltinFont(baseFont, obj, descriptor); } } else if (subType.equals("Type3")) { // load a type 3 font font = new Type3Font(baseFont, obj, resources, descriptor); } else if (subType.equals("CIDFontType2")) { font = new CIDFontType2(baseFont, obj, descriptor); } else if (subType.equals("CIDFontType0")) { font = new CIDFontType2(baseFont, obj, descriptor); // font = new CIDFontType0(baseFont, obj, descriptor); // throw new IOException ("CIDFontType0 is unimplemented. " + obj); } else { throw new PDFParseException("Don't know how to handle a '" + subType + "' font"); } */ font.setSubtype(subType); font.setEncoding(encoding); font.setUnicodeMap(toUnicodeM); obj.setCache(font); return font; } /** * Get the subtype of this font. * @return the subtype, one of: Type0, Type1, TrueType or Type3 */ public String getSubtype() { return subtype; } /** * Set the font subtype */ public void setSubtype(String subtype) { this.subtype = subtype; } /** * Get the postscript name of this font * @return the postscript name of this font */ public String getBaseFont() { return baseFont; } /** * Set the postscript name of this font * @param baseFont the postscript name of the font */ public void setBaseFont(String baseFont) { this.baseFont = baseFont; } /** * Get the encoding for this font * @return the encoding which maps from this font to actual characters */ public PDFFontEncoding getEncoding() { return encoding; } /** * Set the encoding for this font */ public void setEncoding(PDFFontEncoding encoding) { this.encoding = encoding; } /** * Get the descriptor for this font * @return the font descriptor */ public PDFFontDescriptor getDescriptor() { return descriptor; } /** * Set the descriptor font descriptor */ public void setDescriptor(PDFFontDescriptor descriptor) { this.descriptor = descriptor; } /** * Get the CMap which maps the characters in this font to unicode names */ public PDFCMap getUnicodeMap() { return unicodeMap; } /** * Set the CMap which maps the characters in this font to unicode names */ public void setUnicodeMap(PDFCMap unicodeMap) { this.unicodeMap = unicodeMap; } /** * Get the glyphs associated with a given String in this font * * @param text the text to translate into glyphs */ public List<PDFGlyph> getGlyphs(String text) { List<PDFGlyph> outList = null; // if we have an encoding, use it to get the commands if (encoding != null) { outList = encoding.getGlyphs(this, text); } else { // use the default mapping char[] arry = text.toCharArray(); outList = new ArrayList<PDFGlyph>(arry.length); for (int i = 0; i < arry.length; i++) { // only look at 2 bytes when there is no encoding char src = (char)(arry[i] & 0xff); outList.add(getCachedGlyph(src, null)); } } return outList; } /** * Get a glyph for a given character code. The glyph is returned * from the cache if available, or added to the cache if not * * @param src the character code of this glyph * @param name the name of the glyph, or null if the name is unknown * @return a glyph for this character */ public PDFGlyph getCachedGlyph(char src, String name) { if (charCache == null) { charCache = new HashMap<Character,PDFGlyph>(); } // try the cache PDFGlyph glyph = (PDFGlyph) charCache.get(new Character(src)); // if it's not there, add it to the cache if (glyph == null) { glyph = getGlyph(src, name); charCache.put(new Character(src), glyph); } return glyph; } /** * Create a PDFFont given the base font name and the font descriptor * @param baseFont the postscript name of this font * @param descriptor the descriptor for the font */ protected PDFFont(String baseFont, PDFFontDescriptor descriptor) { setBaseFont(baseFont); setDescriptor(descriptor); } /** * Get the glyph for a given character code and name * * The preferred method of getting the glyph should be by name. If the * name is null or not valid, then the character code should be used. * If the both the code and the name are invalid, the undefined glyph * should be returned. * * Note this method must *always* return a glyph. * * @param src the character code of this glyph * @param name the name of this glyph or null if unknown * @return a glyph for this character */ protected abstract PDFGlyph getGlyph(char src, String name); /** * Turn this font into a pretty String */ @Override public String toString() { return getBaseFont(); } /** * Compare two fonts base on the baseFont */ @Override public boolean equals(Object o) { if (!(o instanceof PDFFont)) { return false; } return ((PDFFont) o).getBaseFont().equals(getBaseFont()); } /** * Hash a font based on its base font */ @Override public int hashCode() { return getBaseFont().hashCode(); } }