• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2010 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.typography.font.sfntly.data;
18 
19 import java.io.IOException;
20 import java.io.InputStream;
21 
22 /**
23  * Writable font data wrapper. Supports writing of data primitives in the
24  * TrueType / OpenType spec.
25  *
26  * @author Stuart Gill
27  */
28 public final class WritableFontData extends ReadableFontData {
29 
30   /**
31    * Constructs a writable font data object. If the length is specified as
32    * positive then a fixed size font data object will be created. If the length
33    * is zero or less then a growable font data object will be created and the
34    * size will be used as an estimate to help in allocating the original space.
35    *
36    * @param length if length > 0 create a fixed length font data; otherwise
37    *        create a growable font data
38    * @return a new writable font data
39    */
createWritableFontData(int length)40   public static final WritableFontData createWritableFontData(int length) {
41     ByteArray<?> ba = null;
42     if (length > 0) {
43       ba = new MemoryByteArray(length);
44       ba.setFilledLength(length);
45     } else {
46       ba = new GrowableMemoryByteArray();
47     }
48     WritableFontData wfd = new WritableFontData(ba);
49     return wfd;
50   }
51 
52   /**
53    * Constructs a writable font data object. The new font data object will wrap
54    * the bytes passed in to the factory and it will take ownership of those
55    * bytes. They should not be used again by the caller.
56    *
57    * @param b the byte array to wrap
58    * @return a new writable font data
59    */
createWritableFontData(byte[] b)60   public static final WritableFontData createWritableFontData(byte[] b) {
61     ByteArray<?> ba = new MemoryByteArray(b);
62     WritableFontData wfd = new WritableFontData(ba);
63     return wfd;
64   }
65 
66   /**
67    * Constructs a writable font data object. The new font data object will wrap
68    * a copy of the the data used by the original writable font data object passed in.
69    *
70    * @param original the source font data
71    * @return a new writable font data
72    */
createWritableFontData(ReadableFontData original)73   public static final WritableFontData createWritableFontData(ReadableFontData original) {
74     ByteArray<?> ba = null;
75     // TODO(stuartg): push this down into the BAs - maybe remove the difference between growable and fixed
76     if (original.array.growable()) {
77       ba = new GrowableMemoryByteArray();
78     } else {
79       ba = new MemoryByteArray(original.array.length());
80     }
81     original.array.copyTo(ba);
82 
83     WritableFontData wfd = new WritableFontData(ba);
84     wfd.setCheckSumRanges(original.checkSumRange());
85     return wfd;
86   }
87 
88   /**
89    * Constructor.
90    *
91    * @param array byte array to wrap
92    */
WritableFontData(ByteArray<? extends ByteArray<?>> array)93   private WritableFontData(ByteArray<? extends ByteArray<?>> array) {
94     super(array);
95   }
96 
97   /**
98    * Constructor with a lower bound.
99    *
100    * @param data other WritableFontData object to share data with
101    * @param offset offset from the other WritableFontData's data
102    */
WritableFontData(WritableFontData data, int offset)103   private WritableFontData(WritableFontData data, int offset) {
104     super(data, offset);
105   }
106 
107   /**
108    * Constructor with lower bound and a length bound.
109    *
110    * @param data other WritableFontData object to share data with
111    * @param offset offset from the other WritableFontData's data
112    * @param length length of other WritableFontData's data to use
113    */
WritableFontData(WritableFontData data, int offset, int length)114   private WritableFontData(WritableFontData data, int offset, int length) {
115     super(data, offset, length);
116   }
117 
118   /**
119    * Makes a slice of this FontData. The returned slice will share the data with
120    * the original <code>FontData</code>.
121    *
122    * @param offset the start of the slice
123    * @param length the number of bytes in the slice
124    * @return a slice of the original FontData
125    */
126   @Override
slice(int offset, int length)127   public WritableFontData slice(int offset, int length) {
128     if (offset < 0 || length < 0 || offset > Integer.MAX_VALUE - length ||
129         (offset + length) > this.size()) {
130       throw new IndexOutOfBoundsException("Attempt to bind data outside of its limits.");
131     }
132     WritableFontData slice = new WritableFontData(this, offset, length);
133     return slice;
134   }
135 
136   /**
137    * Makes a bottom bound only slice of this array. The returned slice will
138    * share the data with the original <code>FontData</code>.
139    *
140    * @param offset the start of the slice
141    * @return a slice of the original FontData
142    */
143   @Override
slice(int offset)144   public WritableFontData slice(int offset) {
145     if (offset < 0 || offset > this.size()) {
146       throw new IndexOutOfBoundsException("Attempt to bind data outside of its limits.");
147     }
148     WritableFontData slice = new WritableFontData(this, offset);
149     return slice;
150   }
151 
152   /**
153    * Writes a byte at the given index.
154    *
155    * @param index index into the font data
156    * @param b the byte to write
157    * @return the number of bytes written
158    */
writeByte(int index, byte b)159   public int writeByte(int index, byte b) {
160     this.array.put(this.boundOffset(index), b);
161     return 1;
162   }
163 
164   /**
165    * Writes the bytes from the array.
166    *
167    * @param index index into the font data
168    * @param b the source for the bytes to be written
169    * @param offset offset in the byte array
170    * @param length the length of the bytes to be written
171    * @return the number of bytes actually written; -1 if the index is outside
172    *         the FontData's range
173    */
writeBytes(int index, byte[] b, int offset, int length)174   public int writeBytes(int index, byte[] b, int offset, int length) {
175     return this.array.put(this.boundOffset(index), b, offset, this.boundLength(index, length));
176   }
177 
178   /**
179    * Writes the bytes from the array and pad if necessary.
180    *
181    *  Writes to the length given using the byte array provided and if there are
182    * not enough bytes in the array then pad to the requested length using the
183    * pad byte specified.
184    *
185    * @param index index into the font data
186    * @param b the source for the bytes to be written
187    * @param offset offset in the byte array
188    * @param length the length of the bytes to be written
189    * @param pad the padding byte to be used if necessary
190    * @return the number of bytes actually written
191    */
writeBytesPad(int index, byte[] b, int offset, int length, byte pad)192   public int writeBytesPad(int index, byte[] b, int offset, int length, byte pad) {
193     int written = this.array.put(this.boundOffset(index), b, offset,
194         this.boundLength(index, Math.min(length, b.length - offset)));
195     written += this.writePadding(written + index, length - written, pad);
196     return written;
197   }
198 
199   /**
200    * Writes padding to the FontData. The padding byte written is 0x00.
201    *
202    * @param index index into the font data
203    * @param count the number of pad bytes to write
204    * @return the number of pad bytes written
205    */
writePadding(int index, int count)206   public int writePadding(int index, int count) {
207     return this.writePadding(index, count, (byte) 0x00);
208   }
209 
210   /**
211    * Writes padding to the FontData.
212    *
213    * @param index index into the font data
214    * @param count the number of pad bytes to write
215    * @param pad the byte value to use as padding
216    * @return the number of pad bytes written
217    */
writePadding(int index, int count, byte pad)218   public int writePadding(int index, int count, byte pad) {
219     for (int i = 0; i < count; i++) {
220       this.array.put(index + i, pad);
221     }
222     return count;
223   }
224 
225   /**
226    * Writes the bytes from the array.
227    *
228    * @param index index into the font data
229    * @param b the source for the bytes to be written
230    * @return the number of bytes actually written; -1 if the index is outside
231    *         the FontData's range
232    */
writeBytes(int index, byte[] b)233   public int writeBytes(int index, byte[] b) {
234     return this.writeBytes(index, b, 0, b.length);
235   }
236 
237   /**
238    * Writes the CHAR at the given index.
239    *
240    * @param index index into the font data
241    * @param c the CHAR
242    * @return the number of bytes actually written
243    * @throws IndexOutOfBoundsException if index is outside the FontData's range
244    */
writeChar(int index, byte c)245   public int writeChar(int index, byte c) {
246     return this.writeByte(index, c);
247   }
248 
249   /**
250    * Writes the USHORT at the given index.
251    *
252    * @param index index into the font data
253    * @param us the USHORT
254    * @return the number of bytes actually written
255    * @throws IndexOutOfBoundsException if index is outside the FontData's range
256    */
writeUShort(int index, int us)257   public int writeUShort(int index, int us) {
258     this.writeByte(index, (byte) ((us >> 8) & 0xff));
259     this.writeByte(index + 1, (byte) (us & 0xff));
260     return 2;
261   }
262 
263   /**
264    * Writes the USHORT at the given index in little endian format.
265    *
266    * @param index index into the font data
267    * @param us the USHORT
268    * @return the number of bytes actually written
269    * @throws IndexOutOfBoundsException if index is outside the FontData's range
270    */
writeUShortLE(int index, int us)271   public int writeUShortLE(int index, int us) {
272     this.array.put(index, (byte) (us & 0xff));
273     this.array.put(index + 1, (byte) ((us >> 8) & 0xff));
274     return 2;
275   }
276 
277   /**
278    * Writes the SHORT at the given index.
279    *
280    * @param index index into the font data
281    * @param s the SHORT
282    * @return the number of bytes actually written
283    * @throws IndexOutOfBoundsException if index is outside the FontData's range
284    */
writeShort(int index, int s)285   public int writeShort(int index, int s) {
286     return this.writeUShort(index, s);
287   }
288 
289   /**
290    * Writes the UINT24 at the given index.
291    *
292    * @param index index into the font data
293    * @param ui the UINT24
294    * @return the number of bytes actually written
295    * @throws IndexOutOfBoundsException if index is outside the FontData's range
296    */
writeUInt24(int index, int ui)297   public int writeUInt24(int index, int ui) {
298     this.writeByte(index, (byte) ((ui >> 16) & 0xff));
299     this.writeByte(index + 1, (byte) ((ui >> 8) & 0xff));
300     this.writeByte(index + 2, (byte) (ui & 0xff));
301     return 3;
302   }
303 
304   /**
305    * Writes the ULONG at the given index.
306    *
307    * @param index index into the font data
308    * @param ul the ULONG
309    * @return the number of bytes actually written
310    * @throws IndexOutOfBoundsException if index is outside the FontData's range
311    */
writeULong(int index, long ul)312   public int writeULong(int index, long ul) {
313     this.writeByte(index, (byte) ((ul >> 24) & 0xff));
314     this.writeByte(index + 1, (byte) ((ul >> 16) & 0xff));
315     this.writeByte(index + 2, (byte) ((ul >> 8) & 0xff));
316     this.writeByte(index + 3, (byte) (ul & 0xff));
317     return 4;
318   }
319 
320   /**
321    * Writes the ULONG at the given index in little endian format.
322    *
323    * @param index index into the font data
324    * @param ul the ULONG
325    * @return the number of bytes actually written
326    * @throws IndexOutOfBoundsException if index is outside the FontData's range
327    */
writeULongLE(int index, long ul)328   public int writeULongLE(int index, long ul) {
329     this.array.put(index, (byte) (ul & 0xff));
330     this.array.put(index + 1, (byte) ((ul >> 8) & 0xff));
331     this.array.put(index + 2, (byte) ((ul >> 16) & 0xff));
332     this.array.put(index + 3, (byte) ((ul >> 24) & 0xff));
333     return 4;
334   }
335 
336   /**
337    * Writes the LONG at the given index.
338    *
339    * @param index index into the font data
340    * @param l the LONG
341    * @return the number of bytes actually written
342    * @throws IndexOutOfBoundsException if index is outside the FontData's range
343    */
writeLong(int index, long l)344   public int writeLong(int index, long l) {
345     return this.writeULong(index, l);
346   }
347 
348   /**
349    * Writes the Fixed at the given index.
350    *
351    * @param index index into the font data
352    * @param f the Fixed
353    * @return the number of bytes actually written
354    * @throws IndexOutOfBoundsException if index is outside the FontData's range
355    */
writeFixed(int index, int f)356   public int writeFixed(int index, int f) {
357     return this.writeLong(index, f);
358   }
359 
360   /**
361    * Writes the LONGDATETIME at the given index.
362    *
363    * @param index index into the font data
364    * @param date the LONGDATETIME
365    * @return the number of bytes actually written
366    * @throws IndexOutOfBoundsException if index is outside the FontData's range
367    */
writeDateTime(int index, long date)368   public int writeDateTime(int index, long date) {
369     this.writeULong(index, (date >> 32) & 0xffffffff);
370     this.writeULong(index + 4, date & 0xffffffff);
371     return 8;
372   }
373 
374   /**
375    * Copy from the InputStream into this FontData.
376    *
377    * @param is the source
378    * @param length the number of bytes to copy
379    * @throws IOException
380    */
copyFrom(InputStream is, int length)381   public void copyFrom(InputStream is, int length) throws IOException {
382     this.array.copyFrom(is, length);
383   }
384 
385   /**
386    * Copy everything from the InputStream into this FontData.
387    *
388    * @param is the source
389    * @throws IOException
390    */
copyFrom(InputStream is)391   public void copyFrom(InputStream is) throws IOException {
392     this.array.copyFrom(is);
393   }
394 }
395