/*
 * 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; } } }