/*
* 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: PreloaderEPS.java 1610846 2014-07-15 20:44:18Z vhennebert $ */
package org.apache.xmlgraphics.image.loader.impl;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.nio.ByteOrder;
import javax.imageio.stream.ImageInputStream;
import javax.xml.transform.Source;
import org.apache.xmlgraphics.image.loader.ImageContext;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageSize;
import org.apache.xmlgraphics.image.loader.util.ImageInputStreamAdapter;
import org.apache.xmlgraphics.image.loader.util.ImageUtil;
import org.apache.xmlgraphics.ps.DSCConstants;
import org.apache.xmlgraphics.ps.dsc.DSCException;
import org.apache.xmlgraphics.ps.dsc.DSCParser;
import org.apache.xmlgraphics.ps.dsc.DSCParserConstants;
import org.apache.xmlgraphics.ps.dsc.events.DSCComment;
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox;
import org.apache.xmlgraphics.ps.dsc.events.DSCEvent;
import org.apache.xmlgraphics.util.MimeConstants;
Image preloader for EPS images (Encapsulated PostScript).
/**
* Image preloader for EPS images (Encapsulated PostScript).
*/
public class PreloaderEPS extends AbstractImagePreloader {
Key for binary header object used in custom objects of the ImageInfo class. /** Key for binary header object used in custom objects of the ImageInfo class. */
public static final Object EPS_BINARY_HEADER = EPSBinaryFileHeader.class;
Key for bounding box used in custom objects of the ImageInfo class. /** Key for bounding box used in custom objects of the ImageInfo class. */
public static final Object EPS_BOUNDING_BOX = Rectangle2D.class;
{@inheritDoc} /** {@inheritDoc} */
public ImageInfo preloadImage(String uri, Source src, ImageContext context)
throws IOException {
if (!ImageUtil.hasImageInputStream(src)) {
return null;
}
ImageInputStream in = ImageUtil.needImageInputStream(src);
in.mark();
ByteOrder originalByteOrder = in.getByteOrder();
in.setByteOrder(ByteOrder.LITTLE_ENDIAN);
EPSBinaryFileHeader binaryHeader = null;
try {
long magic = in.readUnsignedInt();
magic &= 0xFFFFFFFFL; //Work-around for bug in Java 1.4.2
// Check if binary header
boolean supported = false;
if (magic == 0xC6D3D0C5L) {
supported = true; //binary EPS
binaryHeader = readBinaryFileHeader(in);
in.reset();
in.mark(); //Mark start of file again
in.seek(binaryHeader.psStart);
} else if (magic == 0x53502125L) { //"%!PS" in little endian
supported = true; //ascii EPS
in.reset();
in.mark(); //Mark start of file again
} else {
in.reset();
}
if (supported) {
ImageInfo info = new ImageInfo(uri, MimeConstants.MIME_EPS);
boolean success = determineSize(in, context, info);
in.reset(); //Need to go back to start of file
if (!success) {
//No BoundingBox found, so probably no EPS
return null;
}
if (in.getStreamPosition() != 0) {
throw new IllegalStateException("Need to be at the start of the file here");
}
if (binaryHeader != null) {
info.getCustomObjects().put(EPS_BINARY_HEADER, binaryHeader);
}
return info;
} else {
return null;
}
} finally {
in.setByteOrder(originalByteOrder);
}
}
private EPSBinaryFileHeader readBinaryFileHeader(ImageInputStream in) throws IOException {
EPSBinaryFileHeader offsets = new EPSBinaryFileHeader();
offsets.psStart = in.readUnsignedInt();
offsets.psLength = in.readUnsignedInt();
offsets.wmfStart = in.readUnsignedInt();
offsets.wmfLength = in.readUnsignedInt();
offsets.tiffStart = in.readUnsignedInt();
offsets.tiffLength = in.readUnsignedInt();
return offsets;
}
private boolean determineSize(ImageInputStream in, ImageContext context, ImageInfo info)
throws IOException {
in.mark();
try {
Rectangle2D bbox = null;
DSCParser parser;
try {
parser = new DSCParser(new ImageInputStreamAdapter(in));
outerLoop:
while (parser.hasNext()) {
DSCEvent event = parser.nextEvent();
switch (event.getEventType()) {
case DSCParserConstants.HEADER_COMMENT:
case DSCParserConstants.COMMENT:
//ignore
break;
case DSCParserConstants.DSC_COMMENT:
DSCComment comment = event.asDSCComment();
if (comment instanceof DSCCommentBoundingBox) {
DSCCommentBoundingBox bboxComment = (DSCCommentBoundingBox)comment;
if (DSCConstants.BBOX.equals(bboxComment.getName()) && bbox == null) {
bbox = (Rectangle2D)bboxComment.getBoundingBox().clone();
//BoundingBox is good but HiRes is better so continue
} else if (DSCConstants.HIRES_BBOX.equals(bboxComment.getName())) {
bbox = (Rectangle2D)bboxComment.getBoundingBox().clone();
//HiRefBBox is great so stop
break outerLoop;
}
}
break;
default:
//No more header so stop
break outerLoop;
}
}
if (bbox == null) {
return false;
}
} catch (DSCException e) {
throw new IOException("Error while parsing EPS file: " + e.getMessage());
}
ImageSize size = new ImageSize();
size.setSizeInMillipoints(
(int)Math.round(bbox.getWidth() * 1000),
(int)Math.round(bbox.getHeight() * 1000));
size.setResolution(context.getSourceResolution());
size.calcPixelsFromSize();
info.setSize(size);
info.getCustomObjects().put(EPS_BOUNDING_BOX, bbox);
return true;
} finally {
in.reset();
}
}
Holder class for various pointers to the contents of the EPS file.
/**
* Holder class for various pointers to the contents of the EPS file.
*/
public static class EPSBinaryFileHeader {
private long psStart;
private long psLength;
private long wmfStart;
private long wmfLength;
private long tiffStart;
private long tiffLength;
Returns the start offset of the PostScript section.
Returns: the start offset
/**
* Returns the start offset of the PostScript section.
* @return the start offset
*/
public long getPSStart() {
return psStart;
}
Returns the length of the PostScript section.
Returns: the length of the PostScript section (in bytes)
/**
* Returns the length of the PostScript section.
* @return the length of the PostScript section (in bytes)
*/
public long getPSLength() {
return psLength;
}
Indicates whether the EPS has a WMF preview.
Returns: true if there is a WMF preview
/**
* Indicates whether the EPS has a WMF preview.
* @return true if there is a WMF preview
*/
public boolean hasWMFPreview() {
return (wmfStart != 0);
}
Returns the start offset of the WMF preview.
Returns: the start offset (or 0 if there's no WMF preview)
/**
* Returns the start offset of the WMF preview.
* @return the start offset (or 0 if there's no WMF preview)
*/
public long getWMFStart() {
return wmfStart;
}
Returns the length of the WMF preview.
Returns: the length of the WMF preview (in bytes)
/**
* Returns the length of the WMF preview.
* @return the length of the WMF preview (in bytes)
*/
public long getWMFLength() {
return wmfLength;
}
Indicates whether the EPS has a TIFF preview.
Returns: true if there is a TIFF preview
/**
* Indicates whether the EPS has a TIFF preview.
* @return true if there is a TIFF preview
*/
public boolean hasTIFFPreview() {
return (tiffStart != 0);
}
Returns the start offset of the TIFF preview.
Returns: the start offset (or 0 if there's no TIFF preview)
/**
* Returns the start offset of the TIFF preview.
* @return the start offset (or 0 if there's no TIFF preview)
*/
public long getTIFFStart() {
return tiffStart;
}
Returns the length of the TIFF preview.
Returns: the length of the TIFF preview (in bytes)
/**
* Returns the length of the TIFF preview.
* @return the length of the TIFF preview (in bytes)
*/
public long getTIFFLength() {
return tiffLength;
}
}
}