blob: 6b2d704a18647410aa53845b156011e0a338b704 [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.
*/
/* $Id$ */
package org.apache.fop.fonts.truetype;
import java.io.IOException;
import java.util.Map;
/**
* A utility class for working with Adobe Compact Font Format (CFF) data. See
* Adobe Technical Note #5176 "The Compact Font Format Specification" for more
* information.
*/
final class CFFUtil {
private CFFUtil() {
}
/**
* Extract glyph subset of CFF table ENTRY from input font IN according to
* the specified GLYPHS.
* @param in input font file reader
* @param entry directory entry describing CFF table in input file
* @param glyphs map of original glyph indices to subset indices
* @returns an array of bytes representing a well formed CFF table containing
* the specified glyph subset
* @throws IOException in case of an I/O exception when reading from input font
*/
public static byte[] extractGlyphSubset
( FontFileReader in, TTFDirTabEntry entry, Map<Integer, Integer> glyphs )
throws IOException {
// 1. read CFF data from IN, where ENTRY points at start of CFF table
// 2. while reading CFF data, accumulate necessary information to output subset
// of glyphs, where GLYPHS.keySet() enumerates the desired glyphs, and
// GLYPHS.values() (for the keys) enumerates the new (output) glyph indices
// for the desired glyph subset
// 3. return a BLOB containing a well-formed CFF font according to the Adobe
// spec and constrained as needed by http://www.microsoft.com/typography/otspec/cff.htm
long cffOffset = entry.getOffset();
// HEADER
in.seekSet ( cffOffset );
int major = in.readTTFUByte();
int minor = in.readTTFUByte();
int hdrSize = in.readTTFUByte();
int hdrOffSize = in.readTTFUByte();
// Name INDEX
in.seekSet ( cffOffset + hdrSize );
int nameIndexCount = in.readTTFUShort();
if ( nameIndexCount > 0 ) {
int nameIndexOffsetSize = in.readTTFUByte();
long nameIndexOffsets[] = new long [ nameIndexCount + 1 ];
if ( nameIndexOffsetSize == 1 ) {
for ( int i = 0, n = nameIndexCount + 1; i < n; i++ ) {
nameIndexOffsets [ i ] = in.readTTFUByte();
}
} else if ( nameIndexOffsetSize == 2 ) {
for ( int i = 0, n = nameIndexCount + 1; i < n; i++ ) {
nameIndexOffsets [ i ] = in.readTTFUShort();
}
} else if ( nameIndexOffsetSize == 4 ) {
for ( int i = 0, n = nameIndexCount + 1; i < n; i++ ) {
nameIndexOffsets [ i ] = in.readTTFULong();
}
} else {
throw new RuntimeException ( "invalid offset size, got " + nameIndexOffsetSize + ", expected 1, 2, or 4" );
}
int nameIndexDataOffset = in.getCurrentPos() - 1;
String[] names = new String [ nameIndexCount ];
for ( int i = 0, n = names.length, nOffsets = nameIndexOffsets.length; i < n; i++ ) {
assert ( i + 1 ) < nOffsets;
long offCurrent = nameIndexOffsets [ i ];
long offNext = nameIndexOffsets [ i + 1 ];
long numBytes = offNext - offCurrent;
String name;
if ( numBytes > 0 ) {
if ( numBytes < Integer.MAX_VALUE ) {
long nameOffset = nameIndexDataOffset + offCurrent;
if ( nameOffset < Integer.MAX_VALUE ) {
byte[] nameBytes = in.getBytes ( (int) nameOffset, (int) numBytes );
name = new String ( nameBytes, 0, (int) numBytes, "US-ASCII" );
} else {
throw new UnsupportedOperationException ( "unsupported index offset value, got " + nameOffset + ", expected less than " + Integer.MAX_VALUE );
}
} else {
throw new UnsupportedOperationException ( "unsupported indexed data length, got " + numBytes + ", expected less than " + Integer.MAX_VALUE );
}
} else {
name = "";
}
names [ i ] = name;
}
in.seekSet ( nameIndexDataOffset + nameIndexOffsets [ nameIndexCount ] );
}
// Top Dict INDEX
int topDictIndexCount = in.readTTFUShort();
if ( topDictIndexCount > 0 ) {
int topDictIndexOffsetSize = in.readTTFUByte();
long topDictIndexOffsets[] = new long [ topDictIndexCount + 1 ];
if ( topDictIndexOffsetSize == 1 ) {
for ( int i = 0, n = topDictIndexCount + 1; i < n; i++ ) {
topDictIndexOffsets [ i ] = in.readTTFUByte();
}
} else if ( topDictIndexOffsetSize == 2 ) {
for ( int i = 0, n = topDictIndexCount + 1; i < n; i++ ) {
topDictIndexOffsets [ i ] = in.readTTFUShort();
}
} else if ( topDictIndexOffsetSize == 4 ) {
for ( int i = 0, n = topDictIndexCount + 1; i < n; i++ ) {
topDictIndexOffsets [ i ] = in.readTTFULong();
}
} else {
throw new RuntimeException ( "invalid offset size, got " + topDictIndexOffsetSize + ", expected 1, 2, or 4" );
}
int topDictIndexDataOffset = in.getCurrentPos() - 1;
byte[][] topDicts = new byte [ topDictIndexCount ][];
for ( int i = 0, n = topDicts.length, nOffsets = topDictIndexOffsets.length; i < n; i++ ) {
assert ( i + 1 ) < nOffsets;
long offCurrent = topDictIndexOffsets [ i ];
long offNext = topDictIndexOffsets [ i + 1 ];
long numBytes = offNext - offCurrent;
byte[] topDict;
if ( numBytes > 0 ) {
if ( numBytes < Integer.MAX_VALUE ) {
long topDictOffset = topDictIndexDataOffset + offCurrent;
if ( topDictOffset < Integer.MAX_VALUE ) {
byte[] topDictBytes = in.getBytes ( (int) topDictOffset, (int) numBytes );
topDict = topDictBytes;
} else {
throw new UnsupportedOperationException ( "unsupported index offset value, got " + topDictOffset + ", expected less than " + Integer.MAX_VALUE );
}
} else {
throw new UnsupportedOperationException ( "unsupported indexed data length, got " + numBytes + ", expected less than " + Integer.MAX_VALUE );
}
} else {
topDict = new byte [ 0 ];
}
topDicts [ i ] = topDict;
}
in.seekSet ( topDictIndexDataOffset + topDictIndexOffsets [ topDictIndexCount ] );
}
return new byte[] {};
}
}