/* * Copyright 2006-2017 ICEsoft Technologies Canada Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.icepdf.core.util; import org.icepdf.core.io.SeekableByteArrayInputStream; import org.icepdf.core.io.SeekableInput; import org.icepdf.core.pobjects.StringObject; import org.icepdf.core.pobjects.fonts.ofont.Encoding; import java.io.*; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.util.logging.Level; import java.util.logging.Logger; /** * @author Mark Collette * Date: 18-Feb-2005 * Time: 3:53:40 PM */ public class Utils { private static final Logger logger = Logger.getLogger(Utils.class.toString()); /** * Sets the value into the buffer, at the designated offset, using big-endian rules * Callers is responsible to ensure that value will fit into buffer, starting at offset * * @param value to be set into buffer * @param buffer into which value is to be set * @param offset into buffer which value is to be set */ public static void setIntIntoByteArrayBE(int value, byte[] buffer, int offset) { buffer[offset] = (byte) ((value >>> 24) & 0xff); buffer[offset + 1] = (byte) ((value >>> 16) & 0xff); buffer[offset + 2] = (byte) ((value >>> 8) & 0xff); buffer[offset + 3] = (byte) ((value) & 0xff); } /** * Sets the value into the buffer, at the designated offset, using big-endian rules * Callers is responsible to ensure that value will fit into buffer, starting at offset * * @param value to be set into buffer * @param buffer into which value is to be set * @param offset into buffer which value is to be set */ public static void setShortIntoByteArrayBE(short value, byte[] buffer, int offset) { buffer[offset] = (byte) ((value >>> 8) & 0xff); buffer[offset + 1] = (byte) ((value) & 0xff); } /** * @param in InputStream to read from * @param numBytes number of bytes to read to make integral value from [0, 8] * @return Integral value, which is composed of numBytes bytes, read using big-endian rules from in * @throws IOException */ public static long readLongWithVaryingBytesBE(InputStream in, int numBytes) throws IOException { //System.out.println("Utils.readLongWithVaryingBytesBE() numBytes: " + numBytes); long val = 0; for (int i = 0; i < numBytes; i++) { int curr = in.read(); if (curr < 0) throw new EOFException(); val <<= 8; val |= (((long) curr) & ((long) 0xFF)); } return val; } /** * @param in InputStream to read from * @param numBytes number of bytes to read to make integral value from [0, 4] * @return Integral value, which is composed of numBytes bytes, read using big-endian rules from in * @throws IOException */ public static int readIntWithVaryingBytesBE(InputStream in, int numBytes) throws IOException { //System.out.println("Utils.readIntWithVaryingBytesBE() numBytes: " + numBytes); int val = 0; for (int i = 0; i < numBytes; i++) { int curr = in.read(); if (curr < 0) throw new EOFException(); val <<= 8; val |= (curr & 0xFF); } return val; } /** * Write the given int as a 4 byte integer to the given outputStream. * * @param in stream to write byte data to. * @param i integer to convert to bytes. * @throws IOException write error. */ public static void writeInteger(OutputStream in, int i) throws IOException { ByteBuffer bb = ByteBuffer.allocate(4); bb.putInt(i); in.write(bb.array()); } /** * Write the given int as a 8 byte long to the given outputStream. * * @param in stream to write byte data to. * @param i long to convert to bytes. * @throws IOException write error. */ public static void writeLong(OutputStream in, long i) throws IOException { ByteBuffer bb = ByteBuffer.allocate(8); bb.putLong(i); in.write(bb.array()); } public static String convertByteArrayToHexString(byte[] buffer, boolean addSpaceSeparator) { return convertByteArrayToHexString( buffer, 0, buffer.length, addSpaceSeparator, -1, (char) 0); } public static String convertByteArrayToHexString(byte[] buffer, boolean addSpaceSeparator, int addDelimiterEverNBytes, char delimiter) { return convertByteArrayToHexString( buffer, 0, buffer.length, addSpaceSeparator, addDelimiterEverNBytes, delimiter); } public static String byteFormatter(long bytes, boolean si) { int unit = si ? 1000 : 1024; if (bytes < unit) return bytes + " B"; int exp = (int) (Math.log(bytes) / Math.log(unit)); String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i"); return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); } public static String convertByteArrayToHexString( byte[] buffer, int offset, int length, boolean addSpaceSeparator, int addDelimiterEverNBytes, char delimiter) { int presize = length * (addSpaceSeparator ? 3 : 2); if (addDelimiterEverNBytes > 0) presize += (length / addDelimiterEverNBytes); StringBuilder sb = new StringBuilder(presize); int delimiterCount = 0; int end = offset + length; for (int index = offset; index < end; index++) { int currValue = 0; currValue |= (0xff & ((int) buffer[index])); String s = Integer.toHexString(currValue); for (int i = s.length(); i < 2; i++) sb.append('0'); sb.append(s); if (addSpaceSeparator) sb.append(' '); delimiterCount++; if (addDelimiterEverNBytes > 0 && delimiterCount == addDelimiterEverNBytes) { delimiterCount = 0; sb.append(delimiter); } } return sb.toString(); } /** * boolean java.awt.GraphicsEnvironment.isHeadless() does not exist in Java 1.3, * since it was introduced in Java 1.4, so we use reflection to call it, * if it exists. * In the event of not being able to call graphicsEnvironment.isHeadless(), * instead of throwing an Exception, we simply return defaultReturnIfNoMethod * * @param graphicsEnvironment java.awt.GraphicsEnvironment to call isHeadless() on * @param defaultReturnIfNoMethod Value to return if could not call graphicsEnvironment.isHeadless() */ public static boolean reflectGraphicsEnvironmentISHeadlessInstance(Object graphicsEnvironment, boolean defaultReturnIfNoMethod) { try { Class<?> clazz = graphicsEnvironment.getClass(); Method isHeadlessInstanceMethod = clazz.getMethod("isHeadlessInstance", new Class[]{}); if (isHeadlessInstanceMethod != null) { Object ret = isHeadlessInstanceMethod.invoke( graphicsEnvironment); if (ret instanceof Boolean) return (Boolean) ret; } } catch (Throwable t) { logger.log(Level.FINE, "ImageCache: Java 1.4 Headless support not found."); } return defaultReturnIfNoMethod; } public static String getContentAndReplaceInputStream(InputStream[] inArray, boolean convertToHex) { String content = null; try { ByteArrayOutputStream out = new ByteArrayOutputStream(1024); InputStream in = inArray[0]; byte[] buf = new byte[1024]; while (true) { int read = in.read(buf, 0, buf.length); if (read < 0) break; out.write(buf, 0, read); } // while( true ) { // int read = in.read(); // if( read < 0 ) // break; // out.write( read ); // } if (!(in instanceof SeekableInput)) in.close(); out.flush(); out.close(); byte[] data = out.toByteArray(); inArray[0] = new ByteArrayInputStream(data); if (convertToHex) content = Utils.convertByteArrayToHexString(data, true); else content = new String(data); } catch (IOException ioe) { logger.log(Level.FINE, "Problem getting debug string", ioe); } catch (Throwable e) { logger.log(Level.FINE, "Problem getting content stream, skipping"); } return content; } public static String getContentFromSeekableInput(SeekableInput in, boolean convertToHex) { String content = null; try { in.beginThreadAccess(); long position = in.getAbsolutePosition(); ByteArrayOutputStream out = new ByteArrayOutputStream(); /* byte[] buf = new byte[1024]; while( true ) { int read = in.read( buf, 0, buf.length ); if( read < 0 ) break; out.write( buf, 0, read ); } */ while (true) { int read = in.getInputStream().read(); if (read < 0) break; out.write(read); } in.seekAbsolute(position); out.flush(); out.close(); byte[] data = out.toByteArray(); if (convertToHex) content = Utils.convertByteArrayToHexString(data, true); else content = new String(data); } catch (IOException ioe) { logger.log(Level.FINE, "Problem getting debug string"); } finally { in.endThreadAccess(); } return content; } public static SeekableInput replaceInputStreamWithSeekableInput(InputStream in) { if (in instanceof SeekableInput) return (SeekableInput) in; SeekableInput sin = null; try { ByteArrayOutputStream out = new ByteArrayOutputStream(1024); /* byte[] buf = new byte[1024]; while( true ) { int read = in.read( buf, 0, buf.length ); if( read < 0 ) break; out.write( buf, 0, read ); } */ while (true) { int read = in.read(); if (read < 0) break; out.write(read); } in.close(); out.flush(); out.close(); byte[] data = out.toByteArray(); sin = new SeekableByteArrayInputStream(data); } catch (IOException ioe) { logger.log(Level.FINE, "Problem getting debug string"); } return sin; } public static void printMemory(String str) { long total = Runtime.getRuntime().totalMemory(); long free = Runtime.getRuntime().freeMemory(); long used = total - free; System.out.println("MEM " + str + " used: " + (used / 1024) + " KB delta: " + ((used - lastMemUsed) / 1024) + " KB"); lastMemUsed = used; } private static long lastMemUsed = 0; public static int numBytesToHoldBits(int numBits) { int numBytes = (numBits / 8); if ((numBits % 8) > 0) numBytes++; return numBytes; } /** * When converting between String chars and bytes, there's an implied * encoding to be used, dependent on the context and platform. If * none is specified, then String(byte[]) will use the platform's * default encoding. This method is for when encoding is not relevant, * when the String simply holds byte values in each char. * * {@link org.icepdf.core.pobjects.LiteralStringObject} * {@link org.icepdf.core.pobjects.HexStringObject} */ public static byte[] convertByteCharSequenceToByteArray(CharSequence string) { final int max = string.length(); byte[] bytes = new byte[max]; for (int i = 0; i < max; i++) { bytes[i] = (byte) string.charAt(i); } return bytes; } /** * When converting between String chars and bytes, there's an implied * encoding to be used, dependent on the context and platform. If * none is specified, then String(byte[]) will use the platform's * default encoding. This method is for when encoding is not relevant, * when the String simply holds byte values in each char. * * {@link org.icepdf.core.pobjects.LiteralStringObject} * {@link org.icepdf.core.pobjects.HexStringObject} */ public static String convertByteArrayToByteString(byte[] bytes) { final int max = bytes.length; StringBuilder sb = new StringBuilder(max); for (byte aByte : bytes) { int b = ((int) aByte) & 0xFF; sb.append((char) b); } return sb.toString(); } /** * Utility method for decrypting a String object found in a dictionary * as a plaing text. The string can be encrypted as well as octal encoded, * which is handle by this method. * * @param library document library used for encryption handling. * @param stringObject string object to convert to string * @return converted string. */ public static String convertStringObject(Library library, StringObject stringObject) { String convertedStringObject = null; String titleText = stringObject.getDecryptedLiteralString(library.getSecurityManager()); // If the title begins with 254 and 255 we are working with // Octal encoded strings. Check first to make sure that the // title string is not null, or is at least of length 2. if (titleText != null && titleText.length() >= 2 && ((int) titleText.charAt(0)) == 254 && ((int) titleText.charAt(1)) == 255) { StringBuilder sb1 = new StringBuilder(); // convert teh unicode to characters. for (int i = 2; i < titleText.length(); i += 2) { try { int b1 = ((((int) titleText.charAt(i)) & 0xFF) << 8 ) | ((int) titleText.charAt(i + 1)) & 0xFF; //System.err.println(b1 + " " + b2); sb1.append((char) (b1)); } catch (Exception ex) { // intentionally left blank. } } convertedStringObject = sb1.toString(); } else if (titleText != null) { StringBuilder sb = new StringBuilder(); Encoding enc = Encoding.getPDFDoc(); for (int i = 0; i < titleText.length(); i++) { // sb.append(titleText.charAt(i)); // pdf encoding maps char < 24 to '?' or 63. so we'll skip this map. char character = titleText.charAt(i); if (character > 23) { sb.append(enc.get(character)); } else { sb.append(titleText.charAt(i)); } } convertedStringObject = sb.toString(); } return convertedStringObject; } /** * Convert a utf-8 encoded string into into an octal enocded byte[] array. * * @param literalString string to convert. * @return converted string value. */ public static String convertStringToOctal(String literalString) { // scan string ot see if we have any unicode. int length = literalString.length(); boolean foundExtendedAscii = false; for (int i = 0; i < length; i++) { if (literalString.charAt(i) >= 255) { foundExtendedAscii = true; break; } } if (foundExtendedAscii) { char[] octalEncoded = new char[length * 2 + 2]; octalEncoded[0] = 254; octalEncoded[1] = 255; for (int i = 0, j = 2; i < length; i++, j += 2) { octalEncoded[j] = (char) ((literalString.charAt(i) >> 8) & 0xFF); octalEncoded[j + 1] = (char) (literalString.charAt(i) & 0xFF); } return new String(octalEncoded); } else { return literalString; } } }