1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.fontbox.ttf; 18 19 import java.io.IOException; 20 21 /** 22 * An encoding entry for a cmap. 23 * 24 * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a> 25 * @version $Revision: 1.2 $ 26 */ 27 public class CMAPEncodingEntry 28 { 29 30 private int platformId; 31 private int platformEncodingId; 32 private long subTableOffset; 33 private int[] glyphIdToCharacterCode; 34 /** 35 * This will read the required data from the stream. 36 * 37 * @param ttf The font that is being read. 38 * @param data The stream to read the data from. 39 * @throws IOException If there is an error reading the data. 40 */ 41 public void initData( TrueTypeFont ttf, TTFDataStream data ) throws IOException 42 { 43 platformId = data.readUnsignedShort(); 44 platformEncodingId = data.readUnsignedShort(); 45 subTableOffset = data.readUnsignedInt(); 46 } 47 48 /** 49 * This will read the required data from the stream. 50 * 51 * @param ttf The font that is being read. 52 * @param data The stream to read the data from. 53 * @throws IOException If there is an error reading the data. 54 */ 55 public void initSubtable( TrueTypeFont ttf, TTFDataStream data ) throws IOException 56 { 57 data.seek( ttf.getCMAP().getOffset() + subTableOffset ); 58 int subtableFormat = data.readUnsignedShort(); 59 int length = data.readUnsignedShort(); 60 int version = data.readUnsignedShort(); 61 int numGlyphs = ttf.getMaximumProfile().getNumGlyphs(); 62 if( subtableFormat == 0 ) 63 { 64 byte[] glyphMapping = data.read( 256 ); 65 glyphIdToCharacterCode = new int[256]; 66 for( int i=0;i<glyphMapping.length; i++ ) 67 { 68 glyphIdToCharacterCode[i]=(glyphMapping[i]+256)%256; 69 } 70 } 71 else if( subtableFormat == 2 ) 72 { 73 int[] subHeaderKeys = new int[256]; 74 for( int i=0; i<256; i++) 75 { 76 subHeaderKeys[i] = data.readUnsignedShort(); 77 } 78 int firstCode = data.readUnsignedShort(); 79 int entryCount = data.readUnsignedShort(); 80 short idDelta = data.readSignedShort(); 81 int idRangeOffset = data.readUnsignedShort(); 82 //BJL 83 //HMM the TTF spec is not very clear about what is suppose to 84 //happen here. If you know please submit a patch or point 85 //me to some better documentation. 86 throw new IOException( "Not yet implemented:" + subtableFormat ); 87 } 88 else if( subtableFormat == 4 ) 89 { 90 int segCountX2 = data.readUnsignedShort(); 91 int segCount = segCountX2/2; 92 int searchRange = data.readUnsignedShort(); 93 int entrySelector = data.readUnsignedShort(); 94 int rangeShift = data.readUnsignedShort(); 95 int[] endCount = data.readUnsignedShortArray( segCount ); 96 int reservedPad = data.readUnsignedShort(); 97 int[] startCount = data.readUnsignedShortArray( segCount ); 98 int[] idDelta = data.readUnsignedShortArray( segCount ); 99 int[] idRangeOffset = data.readUnsignedShortArray( segCount ); 100 101 //this is the final result 102 //key=glyphId, value is character codes 103 glyphIdToCharacterCode = new int[numGlyphs]; 104 105 long currentPosition = data.getCurrentPosition(); 106 107 for( int i=0; i<segCount; i++ ) 108 { 109 int start = startCount[i]; 110 int end = endCount[i]; 111 int delta = idDelta[i]; 112 int rangeOffset = idRangeOffset[i]; 113 if( start != 65535 && end != 65535 ) 114 { 115 for( int j=start; j<=end; j++ ) 116 { 117 if( rangeOffset == 0 ) 118 { 119 glyphIdToCharacterCode[ ((j+delta)%65536) ]=j; 120 } 121 else 122 { 123 long glyphOffset = currentPosition + 124 ((rangeOffset/2) + //idRangeOffset[i]/2 125 (j-start) + //(c - startCount[i]) 126 (i-segCount))*2; //&idRangeOffset[i]); 127 data.seek( glyphOffset ); 128 int glyphIndex = data.readUnsignedShort(); 129 if( glyphIndex != 0 ) 130 { 131 glyphIndex += delta; 132 glyphIndex = glyphIndex % 65536; 133 if( glyphIdToCharacterCode[glyphIndex] == 0 ) 134 { 135 glyphIdToCharacterCode[glyphIndex] = j; 136 } 137 } 138 139 } 140 } 141 } 142 } 143 } 144 else if( subtableFormat == 6 ) 145 { 146 int firstCode = data.readUnsignedShort(); 147 int entryCount = data.readUnsignedShort(); 148 glyphIdToCharacterCode = new int[numGlyphs]; 149 int[] glyphIdArray = data.readUnsignedShortArray( entryCount ); 150 for( int i=0; i<entryCount; i++) 151 { 152 glyphIdToCharacterCode[glyphIdArray[i]] = firstCode+i; 153 } 154 } 155 else 156 { 157 throw new IOException( "Unknown cmap format:" + subtableFormat ); 158 } 159 } 160 161 162 /** 163 * @return Returns the glyphIdToCharacterCode. 164 */ 165 public int[] getGlyphIdToCharacterCode() 166 { 167 return glyphIdToCharacterCode; 168 } 169 /** 170 * @param glyphIdToCharacterCodeValue The glyphIdToCharacterCode to set. 171 */ 172 public void setGlyphIdToCharacterCode(int[] glyphIdToCharacterCodeValue) 173 { 174 this.glyphIdToCharacterCode = glyphIdToCharacterCodeValue; 175 } 176 177 /** 178 * @return Returns the platformEncodingId. 179 */ 180 public int getPlatformEncodingId() 181 { 182 return platformEncodingId; 183 } 184 /** 185 * @param platformEncodingIdValue The platformEncodingId to set. 186 */ 187 public void setPlatformEncodingId(int platformEncodingIdValue) 188 { 189 this.platformEncodingId = platformEncodingIdValue; 190 } 191 /** 192 * @return Returns the platformId. 193 */ 194 public int getPlatformId() 195 { 196 return platformId; 197 } 198 /** 199 * @param platformIdValue The platformId to set. 200 */ 201 public void setPlatformId(int platformIdValue) 202 { 203 this.platformId = platformIdValue; 204 } 205 }