
org.apache.fontbox.ttf.TTFDataStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fontbox Show documentation
Show all versions of fontbox Show documentation
The Apache FontBox library is an open source Java tool to obtain low level information
from font files. FontBox is a subproject of Apache PDFBox.
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.fontbox.ttf;
import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.TimeZone;
import org.apache.pdfbox.io.RandomAccessRead;
/**
* An abstract class to read a data stream.
*
* @author Ben Litchfield
*/
abstract class TTFDataStream implements Closeable
{
private static final TimeZone TIMEZONE_UTC = TimeZone.getTimeZone("UTC"); // clone before using
TTFDataStream()
{
}
/**
* Read a 16.16 fixed value, where the first 16 bits are the decimal and the last 16 bits are the fraction.
*
* @return A 32 bit value.
* @throws IOException If there is an error reading the data.
*/
public float read32Fixed() throws IOException
{
float retval = readSignedShort();
retval += (readUnsignedShort() / 65536f);
return retval;
}
/**
* Read a fixed length ascii string.
*
* @param length The length of the string to read.
* @return A string of the desired length.
* @throws IOException If there is an error reading the data.
*/
public String readString(int length) throws IOException
{
return readString(length, StandardCharsets.ISO_8859_1);
}
/**
* Read a fixed length string.
*
* @param length The length of the string to read in bytes.
* @param charset The expected character set of the string.
* @return A string of the desired length.
* @throws IOException If there is an error reading the data.
*/
public String readString(int length, Charset charset) throws IOException
{
return new String(read(length), charset);
}
/**
* Read an unsigned byte.
*
* @return An unsigned byte.
* @throws IOException If there is an error reading the data.
*/
public abstract int read() throws IOException;
/**
* Read an unsigned byte.
*
* @return An unsigned byte.
* @throws IOException If there is an error reading the data.
*/
public abstract long readLong() throws IOException;
/**
* Read a signed byte.
*
* @return A signed byte.
* @throws IOException If there is an error reading the data.
*/
public int readSignedByte() throws IOException
{
int signedByte = read();
return signedByte <= 127 ? signedByte : signedByte - 256;
}
/**
* Read an unsigned byte. Similar to {@link #read()}, but throws an exception if EOF is
* unexpectedly reached.
*
* @return An unsigned byte.
* @throws IOException If there is an error reading the data.
*/
public int readUnsignedByte() throws IOException
{
int unsignedByte = read();
if (unsignedByte == -1)
{
throw new EOFException("premature EOF");
}
return unsignedByte;
}
/**
* Read an unsigned integer.
*
* @return An unsigned integer.
* @throws IOException If there is an error reading the data.
*/
public long readUnsignedInt() throws IOException
{
long byte1 = read();
long byte2 = read();
long byte3 = read();
long byte4 = read();
if (byte4 < 0)
{
throw new EOFException("EOF at " + getCurrentPosition() +
", b1: " + byte1 + ", b2: " + byte2 + ", b3: " + byte3 + ", b4: " + byte4);
}
return (byte1 << 24) + (byte2 << 16) + (byte3 << 8) + byte4;
}
/**
* Read an unsigned short.
*
* @return An unsigned short.
* @throws IOException If there is an error reading the data.
*/
public int readUnsignedShort() throws IOException
{
int b1 = read();
int b2 = read();
if ((b1 | b2) < 0)
{
throw new EOFException("EOF at " + getCurrentPosition() + ", b1: " + b1 + ", b2: " + b2);
}
return (b1 << 8) + b2;
}
/**
* Read an unsigned byte array.
*
* @param length the length of the array to be read
* @return An unsigned byte array.
* @throws IOException If there is an error reading the data.
*/
public int[] readUnsignedByteArray(int length) throws IOException
{
int[] array = new int[length];
for (int i = 0; i < length; i++)
{
array[i] = read();
}
return array;
}
/**
* Read an unsigned short array.
*
* @param length The length of the array to read.
* @return An unsigned short array.
* @throws IOException If there is an error reading the data.
*/
public int[] readUnsignedShortArray(int length) throws IOException
{
int[] array = new int[length];
for (int i = 0; i < length; i++)
{
array[i] = readUnsignedShort();
}
return array;
}
/**
* Read a signed short.
*
* @return A signed short.
* @throws IOException If there is an error reading the data.
*/
public short readSignedShort() throws IOException
{
return (short) readUnsignedShort();
}
/**
* Read an eight byte international date.
*
* @return A signed short.
* @throws IOException If there is an error reading the data.
*/
public Calendar readInternationalDate() throws IOException
{
long secondsSince1904 = readLong();
Calendar cal = Calendar.getInstance((TimeZone) TIMEZONE_UTC.clone());
cal.set(1904, 0, 1, 0, 0, 0);
cal.set(Calendar.MILLISECOND, 0);
long millisFor1904 = cal.getTimeInMillis();
millisFor1904 += (secondsSince1904 * 1000);
cal.setTimeInMillis(millisFor1904);
return cal;
}
/**
* Reads a tag, an array of four uint8s used to identify a script, language system, feature,
* or baseline.
*/
public String readTag() throws IOException
{
return new String(read(4), StandardCharsets.US_ASCII);
}
/**
* Seek into the datasource.
*
* @param pos The position to seek to.
* @throws IOException If there is an error seeking to that position.
*/
public abstract void seek(long pos) throws IOException;
/**
* Read a specific number of bytes from the stream.
*
* @param numberOfBytes The number of bytes to read.
* @return The byte buffer.
* @throws IOException If there is an error while reading.
*/
public byte[] read(int numberOfBytes) throws IOException
{
byte[] data = new byte[numberOfBytes];
int amountRead = 0;
int totalAmountRead = 0;
// read at most numberOfBytes bytes from the stream.
while (totalAmountRead < numberOfBytes
&& (amountRead = read(data, totalAmountRead, numberOfBytes - totalAmountRead)) != -1)
{
totalAmountRead += amountRead;
}
if (totalAmountRead == numberOfBytes)
{
return data;
}
else
{
throw new IOException("Unexpected end of TTF stream reached");
}
}
/**
* @see java.io.InputStream#read(byte[], int, int )
*
* @param b The buffer to write to.
* @param off The offset into the buffer.
* @param len The length into the buffer.
*
* @return The number of bytes read, or -1 at the end of the stream
*
* @throws IOException If there is an error reading from the stream.
*/
public abstract int read(byte[] b, int off, int len) throws IOException;
/**
* Creates a view from current position to {@code pos + length}.
* It can be faster than {@code read(length)} if you only need a few bytes.
* {@code SubView.close()} should never close {@code TTFDataStream.this}, only itself.
*
* @return A view or null (caller can use {@link #read} instead). Please close() the result
*/
public RandomAccessRead createSubView(long length)
{
return null;
}
/**
* Get the current position in the stream.
*
* @return The current position in the stream.
* @throws IOException If an error occurs while reading the stream.
*/
public abstract long getCurrentPosition() throws IOException;
/**
* This will get the original data file that was used for this stream.
*
* @return The data that was read from.
* @throws IOException If there is an issue reading the data.
*/
public abstract InputStream getOriginalData() throws IOException;
/**
* This will get the original data size that was used for this stream.
*
* @return The size of the original data.
*/
public abstract long getOriginalDataSize();
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy