blob: bd12c2c7795e33db117c6299b426c68502b50490 [file] [log] [blame]
/*
* 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.
*/
/* $Id$ */
package org.apache.fop.fonts.type1;
import java.io.IOException;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.BufferedInputStream;
//Commons
import org.apache.commons.io.IOUtils;
/**
* This class represents a parser for Adobe Type 1 PFB files.
*
* @see PFBData
*/
public class PFBParser {
private static final byte[] CURRENTFILE_EEXEC;
private static final byte[] CLEARTOMARK;
static {
try {
CURRENTFILE_EEXEC = "currentfile eexec".getBytes("US-ASCII");
CLEARTOMARK = "cleartomark".getBytes("US-ASCII");
} catch (java.io.UnsupportedEncodingException e) {
throw new RuntimeException("Incompatible VM. It doesn't support the US-ASCII encoding");
}
}
/**
* Parses a PFB file into a PFBData object.
* @param url URL to load the PFB file from
* @return PFBData memory representation of the font
* @throws IOException In case of an I/O problem
*/
public PFBData parsePFB(java.net.URL url) throws IOException {
InputStream in = url.openStream();
try {
return parsePFB(in);
} finally {
in.close();
}
}
/**
* Parses a PFB file into a PFBData object.
* @param pfbFile File to load the PFB file from
* @return PFBData memory representation of the font
* @throws IOException In case of an I/O problem
*/
public PFBData parsePFB(java.io.File pfbFile) throws IOException {
InputStream in = new java.io.FileInputStream(pfbFile);
try {
return parsePFB(in);
} finally {
in.close();
}
}
/**
* Parses a PFB file into a PFBData object.
* @param in InputStream to load the PFB file from
* @return PFBData memory representation of the font
* @throws IOException In case of an I/O problem
*/
public PFBData parsePFB(InputStream in) throws IOException {
PFBData pfb = new PFBData();
BufferedInputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
din.mark(32);
int firstByte = din.readUnsignedByte();
din.reset();
if (firstByte == 128) {
pfb.setPFBFormat(PFBData.PFB_PC);
parsePCFormat(pfb, din);
} else {
pfb.setPFBFormat(PFBData.PFB_RAW);
parseRAWFormat(pfb, bin);
}
return pfb;
}
private static int swapInteger(final int value) {
return (((value >> 0) & 0xff) << 24)
+ (((value >> 8) & 0xff) << 16)
+ (((value >> 16) & 0xff) << 8)
+ (((value >> 24) & 0xff) << 0);
}
private void parsePCFormat(PFBData pfb, DataInputStream din) throws IOException {
int segmentHead;
int segmentType;
int bytesRead;
//Read first segment
segmentHead = din.readUnsignedByte();
if (segmentHead != 128) {
throw new IOException("Invalid file format. Expected ASCII 80hex");
}
segmentType = din.readUnsignedByte(); //Read
int len1 = swapInteger(din.readInt());
byte[] headerSegment = new byte[len1];
din.readFully(headerSegment);
pfb.setHeaderSegment(headerSegment);
//Read second segment
segmentHead = din.readUnsignedByte();
if (segmentHead != 128) {
throw new IOException("Invalid file format. Expected ASCII 80hex");
}
segmentType = din.readUnsignedByte();
int len2 = swapInteger(din.readInt());
byte[] encryptedSegment = new byte[len2];
din.readFully(encryptedSegment);
pfb.setEncryptedSegment(encryptedSegment);
//Read third segment
segmentHead = din.readUnsignedByte();
if (segmentHead != 128) {
throw new IOException("Invalid file format. Expected ASCII 80hex");
}
segmentType = din.readUnsignedByte();
int len3 = swapInteger(din.readInt());
byte[] trailerSegment = new byte[len3];
din.readFully(trailerSegment);
pfb.setTrailerSegment(trailerSegment);
//Read EOF indicator
segmentHead = din.readUnsignedByte();
if (segmentHead != 128) {
throw new IOException("Invalid file format. Expected ASCII 80hex");
}
segmentType = din.readUnsignedByte();
if (segmentType != 3) {
throw new IOException("Expected segment type 3, but found: " + segmentType);
}
}
private static final boolean byteCmp(byte[] src, int srcOffset, byte[] cmp) {
for (int i = 0; i < cmp.length; i++) {
// System.out.println("Compare: " + src[srcOffset + i] + " " + cmp[i]);
if (src[srcOffset + i] != cmp[i]) {
return false;
}
}
return true;
}
private void calcLengths(PFBData pfb, byte[] originalData) {
// Calculate length 1 and 3
// System.out.println ("Checking font, size = "+originalData.length);
// Length1 is the size of the initial ascii portion
// search for "currentfile eexec"
// Get the first binary number and search backwards for "eexec"
int len1 = 30;
// System.out.println("Length1="+len1);
while (!byteCmp(originalData, len1 - CURRENTFILE_EEXEC.length, CURRENTFILE_EEXEC)) {
len1++;
}
// Skip newline
len1++;
// Length3 is length of the last portion of the file
int len3 = 0;
len3 -= CLEARTOMARK.length;
while (!byteCmp(originalData, originalData.length + len3, CLEARTOMARK)) {
len3--;
// System.out.println("Len3="+len3);
}
len3 = -len3;
len3++;
// Eat 512 zeroes
int numZeroes = 0;
byte[] ws1 = new byte[]{0x0D}; //CR
byte[] ws2 = new byte[]{0x0A}; //LF
byte[] ws3 = new byte[]{0x30}; //"0"
while ((originalData[originalData.length - len3] == ws1[0]
|| originalData[originalData.length - len3] == ws2[0]
|| originalData[originalData.length - len3] == ws3[0])
&& numZeroes < 512) {
len3++;
if (originalData[originalData.length - len3] == ws3[0]) {
numZeroes++;
}
}
// System.out.println("Length3="+len3);
//Create the 3 segments
byte[] buffer = new byte[len1];
System.arraycopy(originalData, 0, buffer, 0, len1);
pfb.setHeaderSegment(buffer);
int len2 = originalData.length - len3 - len1;
buffer = new byte[len2];
System.arraycopy(originalData, len1, buffer, 0, len2);
pfb.setEncryptedSegment(buffer);
buffer = new byte[len3];
System.arraycopy(originalData, len1 + len2, buffer, 0, len3);
pfb.setTrailerSegment(buffer);
}
private void parseRAWFormat(PFBData pfb, BufferedInputStream bin)
throws IOException {
calcLengths(pfb, IOUtils.toByteArray(bin));
}
}