| /* |
| * 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.fontbox.cff; |
| |
| import java.awt.geom.GeneralPath; |
| import java.io.IOException; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.ConcurrentHashMap; |
| import org.apache.fontbox.EncodedFont; |
| import org.apache.fontbox.type1.Type1CharStringReader; |
| |
| /** |
| * A Type 1-equivalent font program represented in a CFF file. Thread safe. |
| * |
| * @author Villu Ruusmann |
| * @author John Hewson |
| */ |
| public class CFFType1Font extends CFFFont implements EncodedFont |
| { |
| private final Map<String, Object> privateDict = new LinkedHashMap<>(); |
| private CFFEncoding encoding; |
| |
| private final Map<Integer, Type2CharString> charStringCache = |
| new ConcurrentHashMap<>(); |
| |
| private final PrivateType1CharStringReader reader = new PrivateType1CharStringReader(); |
| private Type2CharStringParser charStringParser = null; |
| |
| private int defaultWidthX = Integer.MIN_VALUE; |
| private int nominalWidthX = Integer.MIN_VALUE; |
| private byte[][] localSubrIndex; |
| |
| /** |
| * Private implementation of Type1CharStringReader, because only CFFType1Font can |
| * expose this publicly, as CIDFonts only support this for legacy 'seac' commands. |
| */ |
| private class PrivateType1CharStringReader implements Type1CharStringReader |
| { |
| @Override |
| public Type1CharString getType1CharString(String name) throws IOException |
| { |
| return CFFType1Font.this.getType1CharString(name); |
| } |
| } |
| |
| @Override |
| public GeneralPath getPath(String name) throws IOException |
| { |
| return getType1CharString(name).getPath(); |
| } |
| |
| @Override |
| public float getWidth(String name) throws IOException |
| { |
| return getType1CharString(name).getWidth(); |
| } |
| |
| @Override |
| public boolean hasGlyph(String name) |
| { |
| int sid = getCharset().getSID(name); |
| int gid = getCharset().getGIDForSID(sid); |
| return gid != 0; |
| } |
| |
| /** |
| * Returns the Type 1 charstring for the given PostScript glyph name. |
| * |
| * @param name PostScript glyph name |
| * @return Type1 charstring of the given PostScript glyph name |
| * |
| * @throws IOException if the charstring could not be read |
| */ |
| public Type1CharString getType1CharString(String name) throws IOException |
| { |
| // lookup via charset |
| int gid = nameToGID(name); |
| |
| // lookup in CharStrings INDEX |
| return getType2CharString(gid, name); |
| } |
| |
| /** |
| * Returns the GID for the given PostScript glyph name. |
| * |
| * @param name a PostScript glyph name. |
| * @return GID |
| */ |
| public int nameToGID(String name) |
| { |
| // some fonts have glyphs beyond their encoding, so we look up by charset SID |
| int sid = getCharset().getSID(name); |
| return getCharset().getGIDForSID(sid); |
| } |
| |
| /** |
| * Returns the Type 1 charstring for the given GID. |
| * |
| * @param gid GID |
| * @throws IOException if the charstring could not be read |
| */ |
| @Override |
| public Type2CharString getType2CharString(int gid) throws IOException |
| { |
| String name = "GID+" + gid; // for debugging only |
| return getType2CharString(gid, name); |
| } |
| |
| // Returns the Type 2 charstring for the given GID, with name for debugging |
| private Type2CharString getType2CharString(int gid, String name) throws IOException |
| { |
| Type2CharString type2 = charStringCache.get(gid); |
| if (type2 == null) |
| { |
| byte[] bytes = null; |
| if (gid < charStrings.length) |
| { |
| bytes = charStrings[gid]; |
| } |
| if (bytes == null) |
| { |
| // .notdef |
| bytes = charStrings[0]; |
| } |
| List<Object> type2seq = getParser().parse(bytes, globalSubrIndex, getLocalSubrIndex()); |
| type2 = new Type2CharString(reader, getName(), name, gid, type2seq, getDefaultWidthX(), |
| getNominalWidthX()); |
| charStringCache.put(gid, type2); |
| } |
| return type2; |
| } |
| |
| private Type2CharStringParser getParser() |
| { |
| if (charStringParser == null) |
| { |
| charStringParser = new Type2CharStringParser(getName()); |
| } |
| return charStringParser; |
| } |
| |
| /** |
| * Returns the private dictionary. |
| * |
| * @return the dictionary |
| */ |
| public Map<String, Object> getPrivateDict() |
| { |
| return privateDict; |
| } |
| |
| /** |
| * Adds the given key/value pair to the private dictionary. |
| * |
| * @param name the given key |
| * @param value the given value |
| */ |
| void addToPrivateDict(String name, Object value) |
| { |
| if (value != null) |
| { |
| privateDict.put(name, value); |
| } |
| } |
| |
| /** |
| * Returns the CFFEncoding of the font. |
| * |
| * @return the encoding |
| */ |
| @Override |
| public CFFEncoding getEncoding() |
| { |
| return encoding; |
| } |
| |
| /** |
| * Sets the CFFEncoding of the font. |
| * |
| * @param encoding the given CFFEncoding |
| */ |
| void setEncoding(CFFEncoding encoding) |
| { |
| this.encoding = encoding; |
| } |
| |
| private byte[][] getLocalSubrIndex() |
| { |
| if (localSubrIndex == null) |
| { |
| localSubrIndex = (byte[][]) privateDict.get("Subrs"); |
| } |
| return localSubrIndex; |
| } |
| |
| // helper for looking up keys/values |
| private Object getProperty(String name) |
| { |
| Object topDictValue = topDict.get(name); |
| if (topDictValue != null) |
| { |
| return topDictValue; |
| } |
| return privateDict.get(name); |
| } |
| |
| private int getDefaultWidthX() |
| { |
| if (defaultWidthX == Integer.MIN_VALUE) |
| { |
| Number num = (Number) getProperty("defaultWidthX"); |
| defaultWidthX = num != null ? num.intValue() : 1000; |
| } |
| return defaultWidthX; |
| } |
| |
| private int getNominalWidthX() |
| { |
| if (nominalWidthX == Integer.MIN_VALUE) |
| { |
| Number num = (Number) getProperty("nominalWidthX"); |
| nominalWidthX = num != null ? num.intValue() : 0; |
| } |
| return nominalWidthX; |
| } |
| } |