1 /* 2 * $Id: CmapTable.java,v 1.3 2009-02-12 13:53:57 tomoke Exp $ 3 * 4 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, 5 * Santa Clara, California 95054, U.S.A. All rights reserved. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22 package com.sun.pdfview.font.ttf; 23 24 import java.nio.ByteBuffer; 25 import java.util.Collection; 26 import java.util.Collections; 27 import java.util.Iterator; 28 import java.util.SortedMap; 29 import java.util.TreeMap; 30 31 /** 32 * Represents the TTF "cmap" table 33 * 34 * @author jkaplan 35 */ 36 public class CmapTable extends TrueTypeTable { 37 38 /** Holds value of property version. */ 39 private short version; 40 41 /** 42 * Holds the CMap subtables, sorted properly 43 */ 44 private SortedMap<CmapSubtable,CMap> subtables; 45 46 /** Creates a new instance of CmapTable */ CmapTable()47 protected CmapTable() { 48 super(TrueTypeTable.CMAP_TABLE); 49 50 setVersion((short) 0x0); 51 52 subtables = Collections.synchronizedSortedMap(new TreeMap<CmapSubtable,CMap>()); 53 } 54 55 /** 56 * Add a CMap 57 */ addCMap(short platformID, short platformSpecificID, CMap cMap)58 public void addCMap(short platformID, short platformSpecificID, 59 CMap cMap) { 60 CmapSubtable key = new CmapSubtable(platformID, platformSpecificID); 61 subtables.put(key, cMap); 62 } 63 64 /** 65 * Get a CMap by platform and specific ID 66 */ getCMap(short platformID, short platformSpecificID)67 public CMap getCMap(short platformID, short platformSpecificID) { 68 CmapSubtable key = new CmapSubtable(platformID, platformSpecificID); 69 return (CMap) subtables.get(key); 70 } 71 72 /** 73 * Get all CMaps 74 */ getCMaps()75 public CMap[] getCMaps() { 76 Collection<CMap> c = subtables.values(); 77 CMap[] maps = new CMap[c.size()]; 78 79 c.toArray(maps); 80 81 return maps; 82 } 83 84 /** 85 * Remove a CMap 86 */ removeCMap(short platformID, short platformSpecificID)87 public void removeCMap(short platformID, short platformSpecificID) { 88 CmapSubtable key = new CmapSubtable(platformID, platformSpecificID); 89 subtables.remove(key); 90 } 91 setData(ByteBuffer data)92 @Override public void setData(ByteBuffer data) { 93 setVersion(data.getShort()); 94 95 short numberSubtables = data.getShort(); 96 97 for (int i = 0; i < numberSubtables; i++) { 98 short platformID = data.getShort(); 99 short platformSpecificID = data.getShort(); 100 int offset = data.getInt(); 101 102 data.mark(); 103 104 // get the position from the start of this buffer 105 data.position(offset); 106 107 ByteBuffer mapData = data.slice(); 108 109 data.reset(); 110 111 try { 112 CMap cMap = CMap.getMap(mapData); 113 if (cMap != null) { 114 addCMap(platformID, platformSpecificID, cMap); 115 } 116 } catch (Exception ex) { 117 System.out.println("Error reading map. PlatformID=" + 118 platformID + ", PlatformSpecificID=" + 119 platformSpecificID); 120 System.out.println("Reason: " + ex); 121 } 122 } 123 } 124 getData()125 @Override public ByteBuffer getData() { 126 ByteBuffer buf = ByteBuffer.allocate(getLength()); 127 128 // write the table header 129 buf.putShort(getVersion()); 130 buf.putShort((short) subtables.size()); 131 132 // the current offset to write to, starts at the end of the 133 // subtables 134 int curOffset = 4 + (subtables.size() * 8); 135 136 // write the subtables 137 for (Iterator i = subtables.keySet().iterator(); i.hasNext();) { 138 CmapSubtable cms = (CmapSubtable) i.next(); 139 CMap map = (CMap) subtables.get(cms); 140 141 buf.putShort(cms.platformID); 142 buf.putShort(cms.platformSpecificID); 143 buf.putInt(curOffset); 144 145 curOffset += map.getLength(); 146 } 147 148 // write the tables 149 for (Iterator i = subtables.values().iterator(); i.hasNext();) { 150 CMap map = (CMap) i.next(); 151 buf.put(map.getData()); 152 } 153 154 // reset the position to the start of the buffer 155 buf.flip(); 156 157 return buf; 158 } 159 160 /** 161 * Get the size of the table, in bytes 162 */ getLength()163 @Override public int getLength() { 164 // start with the size of the fixed data 165 int length = 4; 166 167 // add the size of the subtables 168 length += subtables.size() * 8; 169 170 // add the size of the dynamic data 171 for (Iterator i = subtables.values().iterator(); i.hasNext();) { 172 // add the size of the subtable data 173 CMap map = (CMap) i.next(); 174 length += map.getLength(); 175 } 176 177 return length; 178 } 179 180 181 /** Getter for property version. 182 * @return Value of property version. 183 * 184 */ getVersion()185 public short getVersion() { 186 return this.version; 187 } 188 189 /** Setter for property version. 190 * @param version New value of property version. 191 * 192 */ setVersion(short version)193 public void setVersion(short version) { 194 this.version = version; 195 } 196 197 /** 198 * Get the number of tables 199 */ getNumberSubtables()200 public short getNumberSubtables() { 201 return (short) subtables.size(); 202 } 203 204 /** Print a pretty string */ toString()205 @Override public String toString() { 206 StringBuffer buf = new StringBuffer(); 207 String indent = " "; 208 209 buf.append(indent + "Version: " + this.getVersion() + "\n"); 210 buf.append(indent + "NumMaps: " + this.getNumberSubtables() + "\n"); 211 212 for (Iterator i = subtables.keySet().iterator(); i.hasNext();) { 213 CmapSubtable key = (CmapSubtable) i.next(); 214 215 buf.append(indent + "Map: platformID: " + key.platformID + 216 " PlatformSpecificID: " + key.platformSpecificID + "\n"); 217 218 CMap map = (CMap) subtables.get(key); 219 220 buf.append(map.toString()); 221 } 222 223 return buf.toString(); 224 } 225 226 class CmapSubtable implements Comparable { 227 /** 228 * The platformID for this subtable 229 */ 230 short platformID; 231 232 /** 233 * The platform-specific id 234 */ 235 short platformSpecificID; 236 237 /** 238 * Create a Cmap subtable 239 */ CmapSubtable(short platformID, short platformSpecificID)240 protected CmapSubtable(short platformID, short platformSpecificID) { 241 this.platformID = platformID; 242 this.platformSpecificID = platformSpecificID; 243 } 244 245 /** 246 * Compare two subtables 247 */ equals(Object obj)248 @Override public boolean equals(Object obj) { 249 return (compareTo(obj) == 0); 250 } 251 252 /** 253 * Sort ascending by platform ID and then specific ID 254 */ compareTo(Object obj)255 public int compareTo(Object obj) { 256 if (!(obj instanceof CmapSubtable)) { 257 return -1; 258 } 259 260 CmapSubtable cms = (CmapSubtable) obj; 261 if (platformID < cms.platformID) { 262 return -1; 263 } else if (platformID > cms.platformID) { 264 return 1; 265 } else { 266 if (platformSpecificID < cms.platformSpecificID) { 267 return -1; 268 } else if (platformSpecificID > cms.platformSpecificID) { 269 return 1; 270 } else { 271 return 0; 272 } 273 } 274 } 275 } 276 277 } 278