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