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 
18 /* $Id: FontFileReader.java 1356646 2012-07-03 09:46:41Z mehdi $ */
19 
20 package org.apache.fop.fonts.truetype;
21 
22 import java.io.IOException;
23 import java.io.InputStream;
24 
25 import org.apache.commons.io.IOUtils;
26 
27 /**
28  * Reads a TrueType font file into a byte array and
29  * provides file like functions for array access.
30  */
31 public class FontFileReader {
32 
33     private final int fsize; // file size
34     private int current;    // current position in file
35     private final byte[] file;
36 
37     /**
38      * Constructor
39      *
40      * @param in InputStream to read from
41      * @throws IOException In case of an I/O problem
42      */
FontFileReader(InputStream in)43     public FontFileReader(InputStream in) throws IOException {
44         this.file = IOUtils.toByteArray(in);
45         this.fsize = this.file.length;
46         this.current = 0;
47     }
48 
49 
50     /**
51      * Set current file position to offset
52      *
53      * @param offset The new offset to set
54      * @throws IOException In case of an I/O problem
55      */
seekSet(long offset)56     public void seekSet(long offset) throws IOException {
57         if (offset > fsize || offset < 0) {
58             throw new java.io.EOFException("Reached EOF, file size=" + fsize
59                                            + " offset=" + offset);
60         }
61         current = (int)offset;
62     }
63 
64     /**
65      * Skip a given number of bytes.
66      *
67      * @param add The number of bytes to advance
68      * @throws IOException In case of an I/O problem
69      */
skip(long add)70     public void skip(long add) throws IOException {
71         seekSet(current + add);
72     }
73 
74     /**
75      * Returns current file position.
76      *
77      * @return int The current position.
78      */
getCurrentPos()79     public int getCurrentPos() {
80         return current;
81     }
82 
83     /**
84      * Returns the size of the file.
85      *
86      * @return int The filesize
87      */
getFileSize()88     public int getFileSize() {
89         return fsize;
90     }
91 
92     /**
93      * Read 1 byte.
94      *
95      * @return One byte
96      * @throws IOException If EOF is reached
97      */
read()98     private byte read() throws IOException {
99         if (current >= fsize) {
100             throw new java.io.EOFException("Reached EOF, file size=" + fsize);
101         }
102 
103         final byte ret = file[current++];
104         return ret;
105     }
106 
107     /**
108      * Read 1 signed byte.
109      *
110      * @return One byte
111      * @throws IOException If EOF is reached
112      */
readTTFByte()113     public final byte readTTFByte() throws IOException {
114         return read();
115     }
116 
117     /**
118      * Read 1 unsigned byte.
119      *
120      * @return One unsigned byte
121      * @throws IOException If EOF is reached
122      */
readTTFUByte()123     public final int readTTFUByte() throws IOException {
124         final byte buf = read();
125 
126         if (buf < 0) {
127             return (256 + buf);
128         } else {
129             return buf;
130         }
131     }
132 
133     /**
134      * Read 2 bytes signed.
135      *
136      * @return One signed short
137      * @throws IOException If EOF is reached
138      */
readTTFShort()139     public final short readTTFShort() throws IOException {
140         final int ret = (readTTFUByte() << 8) + readTTFUByte();
141         final short sret = (short)ret;
142         return sret;
143     }
144 
145     /**
146      * Read 2 bytes unsigned.
147      *
148      * @return One unsigned short
149      * @throws IOException If EOF is reached
150      */
readTTFUShort()151     public final int readTTFUShort() throws IOException {
152         final int ret = (readTTFUByte() << 8) + readTTFUByte();
153         return ret;
154     }
155 
156     /**
157      * Write a USHort at a given position.
158      *
159      * @param pos The absolute position to write to
160      * @param val The value to write
161      * @throws IOException If EOF is reached
162      */
writeTTFUShort(long pos, int val)163     public final void writeTTFUShort(long pos, int val) throws IOException {
164         if ((pos + 2) > fsize) {
165             throw new java.io.EOFException("Reached EOF");
166         }
167         final byte b1 = (byte)((val >> 8) & 0xff);
168         final byte b2 = (byte)(val & 0xff);
169         final int fileIndex = (int) pos;
170         file[fileIndex] = b1;
171         file[fileIndex + 1] = b2;
172     }
173 
174     /**
175      * Read 2 bytes signed at position pos without changing current position.
176      *
177      * @param pos The absolute position to read from
178      * @return One signed short
179      * @throws IOException If EOF is reached
180      */
readTTFShort(long pos)181     public final short readTTFShort(long pos) throws IOException {
182         final long cp = getCurrentPos();
183         seekSet(pos);
184         final short ret = readTTFShort();
185         seekSet(cp);
186         return ret;
187     }
188 
189     /**
190      * Read 2 bytes unsigned at position pos without changing current position.
191      *
192      * @param pos The absolute position to read from
193      * @return One unsigned short
194      * @throws IOException If EOF is reached
195      */
readTTFUShort(long pos)196     public final int readTTFUShort(long pos) throws IOException {
197         long cp = getCurrentPos();
198         seekSet(pos);
199         int ret = readTTFUShort();
200         seekSet(cp);
201         return ret;
202     }
203 
204     /**
205      * Read 4 bytes.
206      *
207      * @return One signed integer
208      * @throws IOException If EOF is reached
209      */
readTTFLong()210     public final int readTTFLong() throws IOException {
211         long ret = readTTFUByte();    // << 8;
212         ret = (ret << 8) + readTTFUByte();
213         ret = (ret << 8) + readTTFUByte();
214         ret = (ret << 8) + readTTFUByte();
215 
216         return (int)ret;
217     }
218 
219     /**
220      * Read 4 bytes.
221      *
222      * @return One unsigned integer
223      * @throws IOException If EOF is reached
224      */
readTTFULong()225     public final long readTTFULong() throws IOException {
226         long ret = readTTFUByte();
227         ret = (ret << 8) + readTTFUByte();
228         ret = (ret << 8) + readTTFUByte();
229         ret = (ret << 8) + readTTFUByte();
230 
231         return ret;
232     }
233 
234     /**
235      * Read a NUL terminated ISO-8859-1 string.
236      *
237      * @return A String
238      * @throws IOException If EOF is reached
239      */
readTTFString()240     public final String readTTFString() throws IOException {
241         int i = current;
242         while (file[i++] != 0) {
243             if (i >= fsize) {
244                 throw new java.io.EOFException("Reached EOF, file size="
245                                                + fsize);
246             }
247         }
248 
249         byte[] tmp = new byte[i - current - 1];
250         System.arraycopy(file, current, tmp, 0, i - current - 1);
251         return new String(tmp, "ISO-8859-1");
252     }
253 
254 
255     /**
256      * Read an ISO-8859-1 string of len bytes.
257      *
258      * @param len The length of the string to read
259      * @return A String
260      * @throws IOException If EOF is reached
261      */
readTTFString(int len)262     public final String readTTFString(int len) throws IOException {
263         if ((len + current) > fsize) {
264             throw new java.io.EOFException("Reached EOF, file size=" + fsize);
265         }
266 
267         byte[] tmp = new byte[len];
268         System.arraycopy(file, current, tmp, 0, len);
269         current += len;
270         final String encoding;
271         if ((tmp.length > 0) && (tmp[0] == 0)) {
272             encoding = "UTF-16BE";
273         } else {
274             encoding = "ISO-8859-1";
275         }
276         return new String(tmp, encoding);
277     }
278 
279     /**
280      * Read an ISO-8859-1 string of len bytes.
281      *
282      * @param len The length of the string to read
283      * @param encodingID the string encoding id (presently ignored; always uses UTF-16BE)
284      * @return A String
285      * @throws IOException If EOF is reached
286      */
readTTFString(int len, int encodingID)287     public final String readTTFString(int len, int encodingID) throws IOException {
288         if ((len + current) > fsize) {
289             throw new java.io.EOFException("Reached EOF, file size=" + fsize);
290         }
291 
292         byte[] tmp = new byte[len];
293         System.arraycopy(file, current, tmp, 0, len);
294         current += len;
295         final String encoding;
296         encoding = "UTF-16BE"; //Use this for all known encoding IDs for now
297         return new String(tmp, encoding);
298     }
299 
300     /**
301      * Return a copy of the internal array
302      *
303      * @param offset The absolute offset to start reading from
304      * @param length The number of bytes to read
305      * @return An array of bytes
306      * @throws IOException if out of bounds
307      */
getBytes(int offset, int length)308     public byte[] getBytes(int offset,
309                            int length) throws IOException {
310         if ((offset + length) > fsize) {
311             throw new java.io.IOException("Reached EOF");
312         }
313 
314         byte[] ret = new byte[length];
315         System.arraycopy(file, offset, ret, 0, length);
316         return ret;
317     }
318     /**
319      * Returns the full byte array representation of the file.
320      * @return byte array.
321      */
getAllBytes()322     public byte[] getAllBytes() {
323         return file;
324     }
325 }
326