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