001: /*
002: * $Id: CMapFormat4.java,v 1.2 2007/12/20 18:33:30 rbair Exp $
003: *
004: * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005: * Santa Clara, California 95054, U.S.A. All rights reserved.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020: */
021:
022: package com.sun.pdfview.font.ttf;
023:
024: import java.nio.ByteBuffer;
025: import java.util.Collections;
026: import java.util.Iterator;
027: import java.util.SortedMap;
028: import java.util.TreeMap;
029:
030: /**
031: *
032: * @author jkaplan
033: */
034: public class CMapFormat4 extends CMap {
035:
036: /**
037: * The segments and associated data
038: */
039: public SortedMap segments;
040:
041: /** Creates a new instance of CMapFormat0 */
042: protected CMapFormat4(short language) {
043: super ((short) 4, language);
044:
045: segments = Collections.synchronizedSortedMap(new TreeMap());
046:
047: char[] map = new char[1];
048: map[0] = (char) 0;
049: addSegment((short) 0xffff, (short) 0xffff, map);
050: }
051:
052: /**
053: * Add a segment with a map
054: */
055: public void addSegment(short startCode, short endCode, char[] map) {
056: if (map.length != (endCode - startCode) + 1) {
057: throw new IllegalArgumentException(
058: "Wrong number of entries in map");
059: }
060:
061: Segment s = new Segment(startCode, endCode, true);
062: // make sure we remove any old entries
063: segments.remove(s);
064: segments.put(s, map);
065: }
066:
067: /**
068: * Add a segment with an idDelta
069: */
070: public void addSegment(short startCode, short endCode, short idDelta) {
071: Segment s = new Segment(startCode, endCode, false);
072: // make sure we remove any old entries
073: segments.remove(s);
074: segments.put(s, new Integer(idDelta));
075: }
076:
077: /**
078: * Remove a segment
079: */
080: public void removeSegment(short startCode, short endCode) {
081: Segment s = new Segment(startCode, endCode, true);
082: segments.remove(s);
083: }
084:
085: /**
086: * Get the length of this table
087: */
088: public short getLength() {
089: // start with the size of the fixed header
090: short size = 16;
091:
092: // add the size of each segment header
093: size += segments.size() * 8;
094:
095: // add the total number of mappings times the size of a mapping
096: for (Iterator i = segments.keySet().iterator(); i.hasNext();) {
097: Segment s = (Segment) i.next();
098:
099: // see if there's a map
100: if (s.hasMap) {
101: // if there is, add its size
102: char[] map = (char[]) segments.get(s);
103: size += map.length * 2;
104: }
105: }
106:
107: return size;
108: }
109:
110: /**
111: * Cannot map from a byte
112: */
113: public byte map(byte src) {
114: char c = map((char) src);
115: if (c < Byte.MIN_VALUE || c > Byte.MAX_VALUE) {
116: // out of range
117: return 0;
118: }
119:
120: return (byte) c;
121: }
122:
123: /**
124: * Map from char
125: */
126: public char map(char src) {
127: // find first segment with endcode > src
128: for (Iterator i = segments.keySet().iterator(); i.hasNext();) {
129: Segment s = (Segment) i.next();
130:
131: if (s.endCode >= src) {
132: // are we within range?
133: if (s.startCode <= src) {
134: if (s.hasMap) {
135: // return the index of this character in
136: // the segment's map
137: char[] map = (char[]) segments.get(s);
138: return map[src - s.startCode];
139: } else {
140: // return the character code + idDelta
141: Integer idDelta = (Integer) segments.get(s);
142: return (char) (src + idDelta.intValue());
143: }
144: } else {
145: // undefined character
146: return (char) 0;
147: }
148: }
149: }
150:
151: // shouldn't get here!
152: return (char) 0;
153: }
154:
155: /**
156: * Get the src code which maps to the given glyphID
157: */
158: public char reverseMap(short glyphID) {
159: // look at each segment
160: for (Iterator i = segments.keySet().iterator(); i.hasNext();) {
161: Segment s = (Segment) i.next();
162:
163: // see if we have a map or a delta
164: if (s.hasMap) {
165: char[] map = (char[]) segments.get(s);
166:
167: // if we have a map, we have to iterate through it
168: for (int c = 0; c < map.length; c++) {
169: if (map[c] == glyphID) {
170: return (char) (s.startCode + c);
171: }
172: }
173: } else {
174: Integer idDelta = (Integer) segments.get(s);
175:
176: // we can do the math to see if we're in range
177: int start = s.startCode + idDelta.intValue();
178: int end = s.endCode + idDelta.intValue();
179:
180: if (glyphID >= start && glyphID <= end) {
181: // we're in the range
182: return (char) (glyphID - idDelta.intValue());
183: }
184: }
185: }
186:
187: // not found!
188: return (char) 0;
189: }
190:
191: /**
192: * Get the data in this map as a ByteBuffer
193: */
194: public void setData(int length, ByteBuffer data) {
195: // read the table size values
196: short segCount = (short) (data.getShort() / 2);
197: short searchRange = data.getShort();
198: short entrySelector = data.getShort();
199: short rangeShift = data.getShort();
200:
201: // create arrays to store segment info
202: short[] endCodes = new short[segCount];
203: short[] startCodes = new short[segCount];
204: short[] idDeltas = new short[segCount];
205: short[] idRangeOffsets = new short[segCount];
206:
207: // the start of the glyph array
208: int glyphArrayPos = 16 + (8 * segCount);
209:
210: // read the endCodes
211: for (int i = 0; i < segCount; i++) {
212: endCodes[i] = data.getShort();
213: }
214:
215: // read the pad
216: data.getShort();
217:
218: // read the start codes
219: for (int i = 0; i < segCount; i++) {
220: startCodes[i] = data.getShort();
221: }
222:
223: // read the idDeltas
224: for (int i = 0; i < segCount; i++) {
225: idDeltas[i] = data.getShort();
226: }
227:
228: // read the id range offsets
229: for (int i = 0; i < segCount; i++) {
230: idRangeOffsets[i] = data.getShort();
231:
232: // calculate the actual offset
233: if (idRangeOffsets[i] <= 0) {
234: // the easy way
235: addSegment(startCodes[i], endCodes[i], idDeltas[i]);
236: } else {
237: // find the start of the data segment
238: int offset = (data.position() - 2) + idRangeOffsets[i];
239:
240: // get the number of entries in the map
241: int size = (endCodes[i] - startCodes[i]) + 1;
242:
243: // allocate the actual map
244: char[] map = new char[size];
245:
246: // remember our offset
247: data.mark();
248:
249: // read the mappings
250: for (int c = 0; c < size; c++) {
251: data.position(offset + (c * 2));
252: map[c] = data.getChar();
253: }
254:
255: // reset the position
256: data.reset();
257:
258: addSegment(startCodes[i], endCodes[i], map);
259: }
260: }
261: }
262:
263: /**
264: * Get the data in the map as a byte buffer
265: */
266: public ByteBuffer getData() {
267: ByteBuffer buf = ByteBuffer.allocate(getLength());
268:
269: // write the header
270: buf.putShort(getFormat());
271: buf.putShort((short) getLength());
272: buf.putShort(getLanguage());
273:
274: // write the various values
275: buf.putShort((short) (getSegmentCount() * 2));
276: buf.putShort(getSearchRange());
277: buf.putShort(getEntrySelector());
278: buf.putShort(getRangeShift());
279:
280: // write the endCodes
281: for (Iterator i = segments.keySet().iterator(); i.hasNext();) {
282: Segment s = (Segment) i.next();
283: buf.putShort((short) s.endCode);
284: }
285:
286: // write the pad
287: buf.putShort((short) 0);
288:
289: // write the startCodes
290: for (Iterator i = segments.keySet().iterator(); i.hasNext();) {
291: Segment s = (Segment) i.next();
292: buf.putShort((short) s.startCode);
293: }
294:
295: // write the idDeltas for segments using deltas
296: for (Iterator i = segments.keySet().iterator(); i.hasNext();) {
297: Segment s = (Segment) i.next();
298:
299: if (!s.hasMap) {
300: Integer idDelta = (Integer) segments.get(s);
301: buf.putShort(idDelta.shortValue());
302: } else {
303: buf.putShort((short) 0);
304: }
305: }
306:
307: // the start of the glyph array
308: int glyphArrayOffset = 16 + (8 * getSegmentCount());
309:
310: // write the idRangeOffsets and maps for segments using maps
311: for (Iterator i = segments.keySet().iterator(); i.hasNext();) {
312: Segment s = (Segment) i.next();
313:
314: if (s.hasMap) {
315: // first set the offset, which is the number of bytes from the
316: // current position to the current offset
317: buf
318: .putShort((short) (glyphArrayOffset - buf
319: .position()));
320:
321: // remember the current position
322: buf.mark();
323:
324: // move the position to the offset
325: buf.position(glyphArrayOffset);
326:
327: // now write the map
328: char[] map = (char[]) segments.get(s);
329: for (int c = 0; c < map.length; c++) {
330: buf.putChar(map[c]);
331: }
332:
333: // reset the data pointer
334: buf.reset();
335:
336: // update the offset
337: glyphArrayOffset += map.length * 2;
338: } else {
339: buf.putShort((short) 0);
340: }
341: }
342:
343: // make sure we are at the end of the buffer before we flip
344: buf.position(glyphArrayOffset);
345:
346: // reset the data pointer
347: buf.flip();
348:
349: return buf;
350: }
351:
352: /**
353: * Get the segment count
354: */
355: public short getSegmentCount() {
356: return (short) segments.size();
357: }
358:
359: /**
360: * Get the search range
361: */
362: public short getSearchRange() {
363: double pow = Math.floor(Math.log(getSegmentCount())
364: / Math.log(2));
365: double pow2 = Math.pow(2, pow);
366:
367: return (short) (2 * pow2);
368: }
369:
370: /**
371: * Get the entry selector
372: */
373: public short getEntrySelector() {
374: int sr2 = getSearchRange() / 2;
375: return (short) (Math.log(sr2) / Math.log(2));
376: }
377:
378: /**
379: * Get the rangeShift()
380: */
381: public short getRangeShift() {
382: return (short) ((2 * getSegmentCount()) - getSearchRange());
383: }
384:
385: /** Get a pretty string */
386: @Override
387: public String toString() {
388: StringBuffer buf = new StringBuffer();
389: String indent = " ";
390:
391: buf.append(super .toString());
392: buf.append(indent + "SegmentCount : " + getSegmentCount()
393: + "\n");
394: buf
395: .append(indent + "SearchRange : " + getSearchRange()
396: + "\n");
397: buf.append(indent + "EntrySelector: " + getEntrySelector()
398: + "\n");
399: buf.append(indent + "RangeShift : " + getRangeShift() + "\n");
400:
401: for (Iterator i = segments.keySet().iterator(); i.hasNext();) {
402: Segment s = (Segment) i.next();
403:
404: buf.append(indent);
405: buf.append("Segment: " + Integer.toHexString(s.startCode));
406: buf.append("-" + Integer.toHexString(s.endCode) + " ");
407: buf.append("hasMap: " + s.hasMap + " ");
408:
409: if (!s.hasMap) {
410: buf.append("delta: " + segments.get(s));
411: }
412:
413: buf.append("\n");
414: }
415:
416: return buf.toString();
417: }
418:
419: class Segment implements Comparable {
420: /** the end code (highest code in this segment) */
421: int endCode;
422:
423: /** the start code (lowest code in this segment) */
424: int startCode;
425:
426: /** whether it is a map or a delta */
427: boolean hasMap;
428:
429: /** Create a new segment */
430: public Segment(short startCode, short endCode, boolean hasMap) {
431: // convert from unsigned short
432: this .endCode = (0xffff & endCode);
433: this .startCode = (0xffff & startCode);
434:
435: this .hasMap = hasMap;
436: }
437:
438: /** Equals based on compareTo (only compares endCode) */
439: @Override
440: public boolean equals(Object o) {
441: return (compareTo(o) == 0);
442: }
443:
444: /** Segments sort by increasing endCode */
445: public int compareTo(Object o) {
446: if (!(o instanceof Segment)) {
447: return -1;
448: }
449:
450: Segment s = (Segment) o;
451:
452: // if regions overlap at all, declare the segments equal,
453: // to avoid overlap in the segment list
454: if (((s.endCode >= startCode) && (s.endCode <= endCode))
455: || ((s.startCode >= startCode) && (s.startCode <= endCode))) {
456: return 0;
457: }
458: if (endCode > s.endCode) {
459: return 1;
460: } else if (endCode < s.endCode) {
461: return -1;
462: } else {
463: return 0;
464: }
465: }
466: }
467: }
|