/* * 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.cmap; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Iterator; /** * This class represents a CMap file. * * @author Ben Litchfield (ben@benlitchfield.com) * @version $Revision: 1.3 $ */ public class CMap { private int wmode = 0; private String cmapName = null; private String cmapVersion = null; private int cmapType = -1; private String registry = null; private String ordering = null; private int supplement = 0; private List<CodespaceRange> codeSpaceRanges = new ArrayList<CodespaceRange>(); private Map<Integer,String> singleByteMappings = new HashMap<Integer,String>(); private Map<Integer,String> doubleByteMappings = new HashMap<Integer,String>(); private final Map<Integer,String> cid2charMappings = new HashMap<Integer,String>(); private final Map<String,Integer> char2CIDMappings = new HashMap<String,Integer>(); private final List<CIDRange> cidRanges = new LinkedList<CIDRange>(); /** * Creates a new instance of CMap. */ public CMap() { //default constructor } /** * This will tell if this cmap has any one byte mappings. * * @return true If there are any one byte mappings, false otherwise. */ public boolean hasOneByteMappings() { return singleByteMappings.size() > 0; } /** * This will tell if this cmap has any two byte mappings. * * @return true If there are any two byte mappings, false otherwise. */ public boolean hasTwoByteMappings() { return doubleByteMappings.size() > 0; } /** * This will tell if this cmap has any CID mappings. * * @return true If there are any CID mappings, false otherwise. */ public boolean hasCIDMappings() { return !char2CIDMappings.isEmpty() || !cidRanges.isEmpty(); } /** * This will perform a lookup into the map. * * @param code The code used to lookup. * @param offset The offset into the byte array. * @param length The length of the data we are getting. * * @return The string that matches the lookup. */ public String lookup( byte[] code, int offset, int length ) { return lookup(getCodeFromArray(code, offset, length), length); } /** * This will perform a lookup into the map. * * @param code The code used to lookup. * @param length The length of the data we are getting. * * @return The string that matches the lookup. */ public String lookup( int code, int length ) { String result = null; if( length == 1 ) { result = singleByteMappings.get( code ); } else if( length == 2 ) { result = doubleByteMappings.get( code ); } return result; } /** * This will perform a lookup into the CID map. * * @param cid The CID used to lookup. * * @return The string that matches the lookup. */ public String lookupCID(int cid) { if (cid2charMappings.containsKey(cid)) { return cid2charMappings.get(cid); } else { for (CIDRange range : cidRanges) { int ch = range.unmap(cid); if (ch != -1) { return Character.toString((char) ch); } } return null; } } /** * This will perform a lookup into the CID map. * * @param code The code used to lookup. * * @return The CID that matches the lookup. */ public int lookupCID(byte[] code, int offset, int length) { if (isInCodeSpaceRanges(code,offset,length)) { int codeAsInt = getCodeFromArray(code, offset, length); if (char2CIDMappings.containsKey(codeAsInt)) { return char2CIDMappings.get(codeAsInt); } else { for (CIDRange range : cidRanges) { int ch = range.map((char)codeAsInt); if (ch != -1) { return ch; } } return -1; } } return -1; } /** * Convert the given part of a byte array to an integer. * @param data the byte array * @param offset The offset into the byte array. * @param length The length of the data we are getting. * @return the resulting integer */ private int getCodeFromArray( byte[] data, int offset, int length ) { int code = 0; for( int i=0; i<length; i++ ) { code <<= 8; code |= (data[offset+i]+256)%256; } return code; } /** * This will add a mapping. * * @param src The src to the mapping. * @param dest The dest to the mapping. * * @throws IOException if the src is invalid. */ public void addMapping( byte[] src, String dest ) throws IOException { int srcLength = src.length; int intSrc = getCodeFromArray(src, 0, srcLength); if( srcLength == 1 ) { singleByteMappings.put( intSrc, dest ); } else if( srcLength == 2 ) { doubleByteMappings.put( intSrc , dest ); } else { throw new IOException( "Mapping code should be 1 or two bytes and not " + src.length ); } } /** * This will add a CID mapping. * * @param src The CID to the mapping. * @param dest The dest to the mapping. * * @throws IOException if the src is invalid. */ public void addCIDMapping( int src, String dest ) throws IOException { cid2charMappings.put( src, dest ); char2CIDMappings.put( dest, src ); } /** * This will add a CID Range. * * @param src The CID Range to be added. * @param dest The starting cid. * */ public void addCIDRange(char from, char to, int cid) { cidRanges.add(0, new CIDRange(from, to, cid)); } /** * This will add a codespace range. * * @param range A single codespace range. */ public void addCodespaceRange( CodespaceRange range ) { codeSpaceRanges.add( range ); } /** * Getter for property codeSpaceRanges. * * @return Value of property codeSpaceRanges. */ public List<CodespaceRange> getCodeSpaceRanges() { return codeSpaceRanges; } /** * Implementation of the usecmap operator. This will * copy all of the mappings from one cmap to another. * * @param cmap The cmap to load mappings from. */ public void useCmap( CMap cmap ) { this.codeSpaceRanges.addAll( cmap.codeSpaceRanges ); this.singleByteMappings.putAll( cmap.singleByteMappings ); this.doubleByteMappings.putAll( cmap.doubleByteMappings ); } /** * Check whether the given byte array is in codespace ranges or not. * * @param code The byte array to look for in the codespace range. * * @return true if the given byte array is in the codespace range. */ public boolean isInCodeSpaceRanges( byte[] code ) { return isInCodeSpaceRanges(code, 0, code.length); } /** * Check whether the given byte array is in codespace ranges or not. * * @param code The byte array to look for in the codespace range. * @param offset The starting offset within the byte array. * @param length The length of the part of the array. * * @return true if the given byte array is in the codespace range. */ public boolean isInCodeSpaceRanges( byte[] code, int offset, int length ) { Iterator<CodespaceRange> it = codeSpaceRanges.iterator(); while ( it.hasNext() ) { CodespaceRange range = it.next(); if ( range != null && range.isInRange(code, offset, length) ) { return true; } } return false; } /** * Returns the WMode of a CMap. * * 0 represents a horizontal and 1 represents a vertical orientation. * * @return */ public int getWMode() { return wmode; } /** * Sets the WMode of a CMap. * * @param newWMode the new WMode. */ public void setWMode(int newWMode) { wmode = newWMode; } /** * Returns the name of the CMap. * * @return the CMap name. */ public String getName() { return cmapName; } /** * Sets the name of the CMap. * * @param name the CMap name. */ public void setName(String name) { cmapName = name; } /** * Returns the version of the CMap. * * @return the CMap version. */ public String getVersion() { return cmapVersion; } /** * Sets the version of the CMap. * * @param version the CMap version. */ public void setVersion(String version) { cmapVersion = version; } /** * Returns the type of the CMap. * * @return the CMap type. */ public int getType() { return cmapType; } /** * Sets the type of the CMap. * * @param type the CMap type. */ public void setType(int type) { cmapType = type; } /** * Returns the registry of the CIDSystemInfo. * * @return the registry. */ public String getRegistry() { return registry; } /** * Sets the registry of the CIDSystemInfo. * * @param newRegistry the registry. */ public void setRegistry(String newRegistry) { registry = newRegistry; } /** * Returns the ordering of the CIDSystemInfo. * * @return the ordering. */ public String getOrdering() { return ordering; } /** * Sets the ordering of the CIDSystemInfo. * * @param newOrdering the ordering. */ public void setOrdering(String newOrdering) { ordering = newOrdering; } /** * Returns the supplement of the CIDSystemInfo. * * @return the supplement. */ public int getSupplement() { return supplement; } /** * Sets the supplement of the CIDSystemInfo. * * @param newSupplement the supplement. */ public void setSupplement(int newSupplement) { supplement = newSupplement; } }