blob: 2e41f37500df0a45ba3dab2d0ff8ef5148ba1210 [file] [log] [blame]
/*
* 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.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.fontbox.type1.Type1CharStringReader;
/**
* A Type 0 CIDFont represented in a CFF file. Thread safe.
*
* @author Villu Ruusmann
* @author John Hewson
*/
public class CFFCIDFont extends CFFFont
{
private String registry;
private String ordering;
private int supplement;
private List<Map<String, Object>> fontDictionaries = Collections.emptyList();
private List<Map<String, Object>> privateDictionaries = Collections.emptyList();
private FDSelect fdSelect;
private final Map<Integer, CIDKeyedType2CharString> charStringCache =
new ConcurrentHashMap<>();
private Type2CharStringParser charStringParser = null;
private final PrivateType1CharStringReader reader = new PrivateType1CharStringReader();
/**
* Returns the registry value.
*
* @return the registry
*/
public String getRegistry()
{
return registry;
}
/**
* Sets the registry value.
*
* @param registry the registry to set
*/
void setRegistry(String registry)
{
this.registry = registry;
}
/**
* Returns the ordering value.
*
* @return the ordering
*/
public String getOrdering()
{
return ordering;
}
/**
* Sets the ordering value.
*
* @param ordering the ordering to set
*/
void setOrdering(String ordering)
{
this.ordering = ordering;
}
/**
* Returns the supplement value.
*
* @return the supplement
*/
public int getSupplement()
{
return supplement;
}
/**
* Sets the supplement value.
*
* @param supplement the supplement to set
*/
void setSupplement(int supplement)
{
this.supplement = supplement;
}
/**
* Returns the font dictionaries.
*
* @return the fontDict
*/
public List<Map<String, Object>> getFontDicts()
{
return fontDictionaries;
}
/**
* Sets the font dictionaries.
*
* @param fontDict the fontDict to set
*/
void setFontDict(List<Map<String, Object>> fontDict)
{
this.fontDictionaries = fontDict;
}
/**
* Returns the private dictionary.
*
* @return the privDict
*/
public List<Map<String, Object>> getPrivDicts()
{
return privateDictionaries;
}
/**
* Sets the private dictionary.
*
* @param privDict the privDict to set
*/
void setPrivDict(List<Map<String, Object>> privDict)
{
this.privateDictionaries = privDict;
}
/**
* Returns the fdSelect value.
*
* @return the fdSelect
*/
public FDSelect getFdSelect()
{
return fdSelect;
}
/**
* Sets the fdSelect value.
*
* @param fdSelect the fdSelect to set
*/
void setFdSelect(FDSelect fdSelect)
{
this.fdSelect = fdSelect;
}
/**
* Returns the defaultWidthX for the given GID.
*
* @param gid GID
*/
private int getDefaultWidthX(int gid)
{
int fdArrayIndex = this.fdSelect.getFDIndex(gid);
if (fdArrayIndex == -1 || fdArrayIndex >= this.privateDictionaries.size())
{
return 1000;
}
Map<String, Object> privDict = this.privateDictionaries.get(fdArrayIndex);
return privDict.containsKey("defaultWidthX") ? ((Number)privDict.get("defaultWidthX")).intValue() : 1000;
}
/**
* Returns the nominalWidthX for the given GID.
*
* @param gid GID
*/
private int getNominalWidthX(int gid)
{
int fdArrayIndex = this.fdSelect.getFDIndex(gid);
if (fdArrayIndex == -1 || fdArrayIndex >= this.privateDictionaries.size())
{
return 0;
}
Map<String, Object> privDict = this.privateDictionaries.get(fdArrayIndex);
return privDict.containsKey("nominalWidthX") ? ((Number)privDict.get("nominalWidthX")).intValue() : 0;
}
/**
* Returns the LocalSubrIndex for the given GID.
*
* @param gid GID
*/
private byte[][] getLocalSubrIndex(int gid)
{
int fdArrayIndex = this.fdSelect.getFDIndex(gid);
if (fdArrayIndex == -1 || fdArrayIndex >= this.privateDictionaries.size())
{
return null;
}
Map<String, Object> privDict = this.privateDictionaries.get(fdArrayIndex);
return (byte[][])privDict.get("Subrs");
}
/**
* Returns the Type 2 charstring for the given CID.
*
* @param cid CID
* @throws IOException if the charstring could not be read
*/
@Override
public CIDKeyedType2CharString getType2CharString(int cid) throws IOException
{
CIDKeyedType2CharString type2 = charStringCache.get(cid);
if (type2 == null)
{
int gid = getCharset().getGIDForCID(cid);
byte[] bytes = charStrings[gid];
if (bytes == null)
{
bytes = charStrings[0]; // .notdef
}
List<Object> type2seq = getParser().parse(bytes, globalSubrIndex,
getLocalSubrIndex(gid));
type2 = new CIDKeyedType2CharString(reader, getName(), cid, gid, type2seq,
getDefaultWidthX(gid), getNominalWidthX(gid));
charStringCache.put(cid, type2);
}
return type2;
}
private Type2CharStringParser getParser()
{
if (charStringParser == null)
{
charStringParser = new Type2CharStringParser(getName());
}
return charStringParser;
}
@Override
public GeneralPath getPath(String selector) throws IOException
{
int cid = selectorToCID(selector);
return getType2CharString(cid).getPath();
}
@Override
public float getWidth(String selector) throws IOException
{
int cid = selectorToCID(selector);
return getType2CharString(cid).getWidth();
}
@Override
public boolean hasGlyph(String selector) throws IOException
{
int cid = selectorToCID(selector);
return cid != 0;
}
/**
* Parses a CID selector of the form \ddddd.
*/
private int selectorToCID(String selector)
{
if (!selector.startsWith("\\"))
{
throw new IllegalArgumentException("Invalid selector");
}
return Integer.parseInt(selector.substring(1));
}
/**
* 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 CFFCIDFont.this.getType2CharString(0); // .notdef
}
}
}