001/* 002 * $Id: RandomAccessFileOrArray.java 4882 2011-05-24 16:33:21Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import com.itextpdf.text.Document; 047import java.io.ByteArrayOutputStream; 048import java.io.DataInput; 049import java.io.DataInputStream; 050import java.io.EOFException; 051import java.io.File; 052import java.io.FileInputStream; 053import java.io.IOException; 054import java.io.InputStream; 055import java.io.RandomAccessFile; 056import java.net.URL; 057import java.nio.channels.FileChannel; 058import com.itextpdf.text.error_messages.MessageLocalization; 059/** An implementation of a RandomAccessFile for input only 060 * that accepts a file or a byte array as data source. 061 * 062 * @author Paulo Soares 063 */ 064public class RandomAccessFileOrArray implements DataInput { 065 066 MappedRandomAccessFile rf; 067 RandomAccessFile trf; 068 boolean plainRandomAccess; 069 String filename; 070 byte arrayIn[]; 071 int arrayInPtr; 072 byte back; 073 boolean isBack = false; 074 075 /** Holds value of property startOffset. */ 076 private int startOffset = 0; 077 078 public RandomAccessFileOrArray(String filename) throws IOException { 079 this(filename, false, Document.plainRandomAccess); 080 } 081 082 /** 083 * Constructs a new RandomAccessFileOrArrayObject 084 * @param filename the file to open (can be a file system file or one of hte following url strings: file://, http://, https://, jar:, wsjar: 085 * @param forceRead if true, the entire file will be read into memory 086 * @param plainRandomAccess if true, a regular RandomAccessFile is used to access the file contents. If false, a memory mapped file will be used, unless the file cannot be mapped into memory, in which case regular RandomAccessFile will be used 087 * @throws IOException if there is a failure opening or reading the file 088 */ 089 public RandomAccessFileOrArray(String filename, boolean forceRead, boolean plainRandomAccess) throws IOException { 090 this.plainRandomAccess = plainRandomAccess; 091 File file = new File(filename); 092 if (!file.canRead()) { 093 if (filename.startsWith("file:/") || filename.startsWith("http://") 094 || filename.startsWith("https://") || filename.startsWith("jar:") || filename.startsWith("wsjar:")) { 095 InputStream is = new URL(filename).openStream(); 096 try { 097 this.arrayIn = InputStreamToArray(is); 098 return; 099 } 100 finally { 101 try {is.close();}catch(IOException ioe){} 102 } 103 } 104 else { 105 InputStream is = BaseFont.getResourceStream(filename); 106 if (is == null) 107 throw new IOException(MessageLocalization.getComposedMessage("1.not.found.as.file.or.resource", filename)); 108 try { 109 this.arrayIn = InputStreamToArray(is); 110 return; 111 } 112 finally { 113 try {is.close();}catch(IOException ioe){} 114 } 115 } 116 } 117 else if (forceRead) { 118 InputStream s = null; 119 try { 120 s = new FileInputStream(file); 121 this.arrayIn = InputStreamToArray(s); 122 } 123 finally { 124 try {if (s != null) {s.close();}}catch(Exception e){} 125 } 126 return; 127 } 128 this.filename = filename; 129 if (plainRandomAccess){ 130 trf = new RandomAccessFile(filename, "r"); 131 }else{ 132 try{ 133 rf = new MappedRandomAccessFile(filename, "r"); 134 } catch (IOException e){ 135 if (exceptionIsMapFailureException(e)){ 136 this.plainRandomAccess = true; 137 trf = new RandomAccessFile(filename, "r"); 138 } else { 139 throw e; 140 } 141 } 142 } 143 } 144 145 /** 146 * Utility method that determines whether a given IOException is the result 147 * of a failure to map a memory mapped file. It would be better if the runtime 148 * provided a special exception for this case, but it doesn't, so we have to rely 149 * on parsing the exception message. 150 * @param e the exception to check 151 * @return true if the exception was the result of a failure to map a memory mapped file 152 * @since 5.0.3 153 */ 154 private static boolean exceptionIsMapFailureException(IOException e){ 155 if (e.getMessage().indexOf("Map failed") >= 0) 156 return true; 157 158 return false; 159 } 160 161 public RandomAccessFileOrArray(URL url) throws IOException { 162 InputStream is = url.openStream(); 163 try { 164 this.arrayIn = InputStreamToArray(is); 165 } 166 finally { 167 try {is.close();}catch(IOException ioe){} 168 } 169 } 170 171 public RandomAccessFileOrArray(InputStream is) throws IOException { 172 this.arrayIn = InputStreamToArray(is); 173 } 174 175 public static byte[] InputStreamToArray(InputStream is) throws IOException { 176 byte b[] = new byte[8192]; 177 ByteArrayOutputStream out = new ByteArrayOutputStream(); 178 while (true) { 179 int read = is.read(b); 180 if (read < 1) 181 break; 182 out.write(b, 0, read); 183 } 184 out.close(); 185 return out.toByteArray(); 186 } 187 188 public RandomAccessFileOrArray(byte arrayIn[]) { 189 this.arrayIn = arrayIn; 190 } 191 192 public RandomAccessFileOrArray(RandomAccessFileOrArray file) { 193 filename = file.filename; 194 arrayIn = file.arrayIn; 195 startOffset = file.startOffset; 196 plainRandomAccess = file.plainRandomAccess; 197 } 198 199 public void pushBack(byte b) { 200 back = b; 201 isBack = true; 202 } 203 204 public int read() throws IOException { 205 if(isBack) { 206 isBack = false; 207 return back & 0xff; 208 } 209 if (arrayIn == null) 210 return plainRandomAccess ? trf.read() : rf.read(); 211 else { 212 if (arrayInPtr >= arrayIn.length) 213 return -1; 214 return arrayIn[arrayInPtr++] & 0xff; 215 } 216 } 217 218 public int read(byte[] b, int off, int len) throws IOException { 219 if (len == 0) 220 return 0; 221 int n = 0; 222 if (isBack) { 223 isBack = false; 224 if (len == 1) { 225 b[off] = back; 226 return 1; 227 } 228 else { 229 n = 1; 230 b[off++] = back; 231 --len; 232 } 233 } 234 if (arrayIn == null) { 235 return (plainRandomAccess ? trf.read(b, off, len) : rf.read(b, off, len)) + n; 236 } 237 else { 238 if (arrayInPtr >= arrayIn.length) 239 return -1; 240 if (arrayInPtr + len > arrayIn.length) 241 len = arrayIn.length - arrayInPtr; 242 System.arraycopy(arrayIn, arrayInPtr, b, off, len); 243 arrayInPtr += len; 244 return len + n; 245 } 246 } 247 248 public int read(byte b[]) throws IOException { 249 return read(b, 0, b.length); 250 } 251 252 public void readFully(byte b[]) throws IOException { 253 readFully(b, 0, b.length); 254 } 255 256 public void readFully(byte b[], int off, int len) throws IOException { 257 int n = 0; 258 do { 259 int count = read(b, off + n, len - n); 260 if (count < 0) 261 throw new EOFException(); 262 n += count; 263 } while (n < len); 264 } 265 266 public long skip(long n) throws IOException { 267 return skipBytes((int)n); 268 } 269 270 public int skipBytes(int n) throws IOException { 271 if (n <= 0) { 272 return 0; 273 } 274 int adj = 0; 275 if (isBack) { 276 isBack = false; 277 if (n == 1) { 278 return 1; 279 } 280 else { 281 --n; 282 adj = 1; 283 } 284 } 285 int pos; 286 int len; 287 int newpos; 288 289 pos = getFilePointer(); 290 len = length(); 291 newpos = pos + n; 292 if (newpos > len) { 293 newpos = len; 294 } 295 seek(newpos); 296 297 /* return the actual number of bytes skipped */ 298 return newpos - pos + adj; 299 } 300 301 public void reOpen() throws IOException { 302 if (filename != null && rf == null && trf == null) { 303 if (plainRandomAccess) 304 trf = new RandomAccessFile(filename, "r"); 305 else 306 rf = new MappedRandomAccessFile(filename, "r"); 307 } 308 seek(0); 309 } 310 311 protected void insureOpen() throws IOException { 312 if (filename != null && rf == null && trf == null) { 313 reOpen(); 314 } 315 } 316 317 public boolean isOpen() { 318 return (filename == null || rf != null || trf != null); 319 } 320 321 public void close() throws IOException { 322 isBack = false; 323 if (rf != null) { 324 rf.close(); 325 rf = null; 326 // it's very expensive to open a memory mapped file and for the usage pattern of this class 327 // in iText it's faster the next re-openings to be done as a plain random access 328 // file 329 plainRandomAccess = true; 330 } 331 else if (trf != null) { 332 trf.close(); 333 trf = null; 334 } 335 } 336 337 public int length() throws IOException { 338 if (arrayIn == null) { 339 insureOpen(); 340 return (int)(plainRandomAccess ? trf.length() : rf.length()) - startOffset; 341 } 342 else 343 return arrayIn.length - startOffset; 344 } 345 346 public void seek(int pos) throws IOException { 347 pos += startOffset; 348 isBack = false; 349 if (arrayIn == null) { 350 insureOpen(); 351 if (plainRandomAccess) 352 trf.seek(pos); 353 else 354 rf.seek(pos); 355 } 356 else 357 arrayInPtr = pos; 358 } 359 360 public void seek(long pos) throws IOException { 361 seek((int)pos); 362 } 363 364 public int getFilePointer() throws IOException { 365 insureOpen(); 366 int n = isBack ? 1 : 0; 367 if (arrayIn == null) { 368 return (int)(plainRandomAccess ? trf.getFilePointer() : rf.getFilePointer()) - n - startOffset; 369 } 370 else 371 return arrayInPtr - n - startOffset; 372 } 373 374 public boolean readBoolean() throws IOException { 375 int ch = this.read(); 376 if (ch < 0) 377 throw new EOFException(); 378 return (ch != 0); 379 } 380 381 public byte readByte() throws IOException { 382 int ch = this.read(); 383 if (ch < 0) 384 throw new EOFException(); 385 return (byte)(ch); 386 } 387 388 public int readUnsignedByte() throws IOException { 389 int ch = this.read(); 390 if (ch < 0) 391 throw new EOFException(); 392 return ch; 393 } 394 395 public short readShort() throws IOException { 396 int ch1 = this.read(); 397 int ch2 = this.read(); 398 if ((ch1 | ch2) < 0) 399 throw new EOFException(); 400 return (short)((ch1 << 8) + ch2); 401 } 402 403 /** 404 * Reads a signed 16-bit number from this stream in little-endian order. 405 * The method reads two 406 * bytes from this stream, starting at the current stream pointer. 407 * If the two bytes read, in order, are 408 * <code>b1</code> and <code>b2</code>, where each of the two values is 409 * between <code>0</code> and <code>255</code>, inclusive, then the 410 * result is equal to: 411 * <blockquote><pre> 412 * (short)((b2 << 8) | b1) 413 * </pre></blockquote> 414 * <p> 415 * This method blocks until the two bytes are read, the end of the 416 * stream is detected, or an exception is thrown. 417 * 418 * @return the next two bytes of this stream, interpreted as a signed 419 * 16-bit number. 420 * @exception EOFException if this stream reaches the end before reading 421 * two bytes. 422 * @exception IOException if an I/O error occurs. 423 */ 424 public final short readShortLE() throws IOException { 425 int ch1 = this.read(); 426 int ch2 = this.read(); 427 if ((ch1 | ch2) < 0) 428 throw new EOFException(); 429 return (short)((ch2 << 8) + (ch1 << 0)); 430 } 431 432 public int readUnsignedShort() throws IOException { 433 int ch1 = this.read(); 434 int ch2 = this.read(); 435 if ((ch1 | ch2) < 0) 436 throw new EOFException(); 437 return (ch1 << 8) + ch2; 438 } 439 440 /** 441 * Reads an unsigned 16-bit number from this stream in little-endian order. 442 * This method reads 443 * two bytes from the stream, starting at the current stream pointer. 444 * If the bytes read, in order, are 445 * <code>b1</code> and <code>b2</code>, where 446 * <code>0 <= b1, b2 <= 255</code>, 447 * then the result is equal to: 448 * <blockquote><pre> 449 * (b2 << 8) | b1 450 * </pre></blockquote> 451 * <p> 452 * This method blocks until the two bytes are read, the end of the 453 * stream is detected, or an exception is thrown. 454 * 455 * @return the next two bytes of this stream, interpreted as an 456 * unsigned 16-bit integer. 457 * @exception EOFException if this stream reaches the end before reading 458 * two bytes. 459 * @exception IOException if an I/O error occurs. 460 */ 461 public final int readUnsignedShortLE() throws IOException { 462 int ch1 = this.read(); 463 int ch2 = this.read(); 464 if ((ch1 | ch2) < 0) 465 throw new EOFException(); 466 return (ch2 << 8) + (ch1 << 0); 467 } 468 469 public char readChar() throws IOException { 470 int ch1 = this.read(); 471 int ch2 = this.read(); 472 if ((ch1 | ch2) < 0) 473 throw new EOFException(); 474 return (char)((ch1 << 8) + ch2); 475 } 476 477 /** 478 * Reads a Unicode character from this stream in little-endian order. 479 * This method reads two 480 * bytes from the stream, starting at the current stream pointer. 481 * If the bytes read, in order, are 482 * <code>b1</code> and <code>b2</code>, where 483 * <code>0 <= b1, b2 <= 255</code>, 484 * then the result is equal to: 485 * <blockquote><pre> 486 * (char)((b2 << 8) | b1) 487 * </pre></blockquote> 488 * <p> 489 * This method blocks until the two bytes are read, the end of the 490 * stream is detected, or an exception is thrown. 491 * 492 * @return the next two bytes of this stream as a Unicode character. 493 * @exception EOFException if this stream reaches the end before reading 494 * two bytes. 495 * @exception IOException if an I/O error occurs. 496 */ 497 public final char readCharLE() throws IOException { 498 int ch1 = this.read(); 499 int ch2 = this.read(); 500 if ((ch1 | ch2) < 0) 501 throw new EOFException(); 502 return (char)((ch2 << 8) + (ch1 << 0)); 503 } 504 505 public int readInt() throws IOException { 506 int ch1 = this.read(); 507 int ch2 = this.read(); 508 int ch3 = this.read(); 509 int ch4 = this.read(); 510 if ((ch1 | ch2 | ch3 | ch4) < 0) 511 throw new EOFException(); 512 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4); 513 } 514 515 /** 516 * Reads a signed 32-bit integer from this stream in little-endian order. 517 * This method reads 4 518 * bytes from the stream, starting at the current stream pointer. 519 * If the bytes read, in order, are <code>b1</code>, 520 * <code>b2</code>, <code>b3</code>, and <code>b4</code>, where 521 * <code>0 <= b1, b2, b3, b4 <= 255</code>, 522 * then the result is equal to: 523 * <blockquote><pre> 524 * (b4 << 24) | (b3 << 16) + (b2 << 8) + b1 525 * </pre></blockquote> 526 * <p> 527 * This method blocks until the four bytes are read, the end of the 528 * stream is detected, or an exception is thrown. 529 * 530 * @return the next four bytes of this stream, interpreted as an 531 * <code>int</code>. 532 * @exception EOFException if this stream reaches the end before reading 533 * four bytes. 534 * @exception IOException if an I/O error occurs. 535 */ 536 public final int readIntLE() throws IOException { 537 int ch1 = this.read(); 538 int ch2 = this.read(); 539 int ch3 = this.read(); 540 int ch4 = this.read(); 541 if ((ch1 | ch2 | ch3 | ch4) < 0) 542 throw new EOFException(); 543 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0)); 544 } 545 546 /** 547 * Reads an unsigned 32-bit integer from this stream. This method reads 4 548 * bytes from the stream, starting at the current stream pointer. 549 * If the bytes read, in order, are <code>b1</code>, 550 * <code>b2</code>, <code>b3</code>, and <code>b4</code>, where 551 * <code>0 <= b1, b2, b3, b4 <= 255</code>, 552 * then the result is equal to: 553 * <blockquote><pre> 554 * (b1 << 24) | (b2 << 16) + (b3 << 8) + b4 555 * </pre></blockquote> 556 * <p> 557 * This method blocks until the four bytes are read, the end of the 558 * stream is detected, or an exception is thrown. 559 * 560 * @return the next four bytes of this stream, interpreted as a 561 * <code>long</code>. 562 * @exception EOFException if this stream reaches the end before reading 563 * four bytes. 564 * @exception IOException if an I/O error occurs. 565 */ 566 public final long readUnsignedInt() throws IOException { 567 long ch1 = this.read(); 568 long ch2 = this.read(); 569 long ch3 = this.read(); 570 long ch4 = this.read(); 571 if ((ch1 | ch2 | ch3 | ch4) < 0) 572 throw new EOFException(); 573 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); 574 } 575 576 public final long readUnsignedIntLE() throws IOException { 577 long ch1 = this.read(); 578 long ch2 = this.read(); 579 long ch3 = this.read(); 580 long ch4 = this.read(); 581 if ((ch1 | ch2 | ch3 | ch4) < 0) 582 throw new EOFException(); 583 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0)); 584 } 585 586 public long readLong() throws IOException { 587 return ((long)(readInt()) << 32) + (readInt() & 0xFFFFFFFFL); 588 } 589 590 public final long readLongLE() throws IOException { 591 int i1 = readIntLE(); 592 int i2 = readIntLE(); 593 return ((long)i2 << 32) + (i1 & 0xFFFFFFFFL); 594 } 595 596 public float readFloat() throws IOException { 597 return Float.intBitsToFloat(readInt()); 598 } 599 600 public final float readFloatLE() throws IOException { 601 return Float.intBitsToFloat(readIntLE()); 602 } 603 604 public double readDouble() throws IOException { 605 return Double.longBitsToDouble(readLong()); 606 } 607 608 public final double readDoubleLE() throws IOException { 609 return Double.longBitsToDouble(readLongLE()); 610 } 611 612 public String readLine() throws IOException { 613 StringBuffer input = new StringBuffer(); 614 int c = -1; 615 boolean eol = false; 616 617 while (!eol) { 618 switch (c = read()) { 619 case -1: 620 case '\n': 621 eol = true; 622 break; 623 case '\r': 624 eol = true; 625 int cur = getFilePointer(); 626 if ((read()) != '\n') { 627 seek(cur); 628 } 629 break; 630 default: 631 input.append((char)c); 632 break; 633 } 634 } 635 636 if ((c == -1) && (input.length() == 0)) { 637 return null; 638 } 639 return input.toString(); 640 } 641 642 public String readUTF() throws IOException { 643 return DataInputStream.readUTF(this); 644 } 645 646 /** Getter for property startOffset. 647 * @return Value of property startOffset. 648 * 649 */ 650 public int getStartOffset() { 651 return this.startOffset; 652 } 653 654 /** Setter for property startOffset. 655 * @param startOffset New value of property startOffset. 656 * 657 */ 658 public void setStartOffset(int startOffset) { 659 this.startOffset = startOffset; 660 } 661 662 /** 663 * @since 2.0.8 664 */ 665 public java.nio.ByteBuffer getNioByteBuffer() throws IOException { 666 if (filename != null) { 667 FileChannel channel; 668 if (plainRandomAccess) 669 channel = trf.getChannel(); 670 else 671 channel = rf.getChannel(); 672 return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); 673 } 674 return java.nio.ByteBuffer.wrap(arrayIn); 675 } 676}