/** {@inheritDoc} */ public void activateLayout() { initialize(); FOUserAgent userAgent = pageSeq.getUserAgent(); ImageManager imageManager = userAgent.getFactory().getImageManager(); String uri = getExternalDocument().getSrc(); Integer firstPageIndex = ImageUtil.getPageIndexFromURI(uri); boolean hasPageIndex = (firstPageIndex != null); try { ImageInfo info = imageManager.getImageInfo(uri, userAgent.getImageSessionContext()); Object moreImages = info.getCustomObjects().get(ImageInfo.HAS_MORE_IMAGES); boolean hasMoreImages = moreImages != null && !Boolean.FALSE.equals(moreImages); Dimension intrinsicSize = info.getSize().getDimensionMpt(); ImageLayout layout = new ImageLayout(getExternalDocument(), this, intrinsicSize); PageSequence pageSequence = new PageSequence(null); transferExtensions(pageSequence); areaTreeHandler.getAreaTreeModel().startPageSequence(pageSequence); if (log.isDebugEnabled()) { log.debug("Starting layout"); } makePageForImage(info, layout); if (!hasPageIndex && hasMoreImages) { if (log.isTraceEnabled()) { log.trace("Starting multi-page processing..."); } URI originalURI; try { originalURI = new URI(URISpecification.escapeURI(uri)); int pageIndex = 1; while (hasMoreImages) { URI tempURI = new URI( originalURI.getScheme(), originalURI.getSchemeSpecificPart(), "page=" + Integer.toString(pageIndex + 1)); if (log.isTraceEnabled()) { log.trace("Subimage: " + tempURI.toASCIIString()); } ImageInfo subinfo = imageManager.getImageInfo( tempURI.toASCIIString(), userAgent.getImageSessionContext()); moreImages = subinfo.getCustomObjects().get(ImageInfo.HAS_MORE_IMAGES); hasMoreImages = moreImages != null && !Boolean.FALSE.equals(moreImages); intrinsicSize = subinfo.getSize().getDimensionMpt(); layout = new ImageLayout(getExternalDocument(), this, intrinsicSize); makePageForImage(subinfo, layout); pageIndex++; } } catch (URISyntaxException e) { getResourceEventProducer().uriError(this, uri, e, getExternalDocument().getLocator()); return; } } } catch (FileNotFoundException fnfe) { getResourceEventProducer().imageNotFound(this, uri, fnfe, getExternalDocument().getLocator()); } catch (IOException ioe) { getResourceEventProducer().imageIOError(this, uri, ioe, getExternalDocument().getLocator()); } catch (ImageException ie) { getResourceEventProducer().imageError(this, uri, ie, getExternalDocument().getLocator()); } }
/** {@inheritDoc} */ @Override public Image loadImage(final ImageInfo info, final Map hints, final ImageSessionContext session) throws ImageException, IOException { if (!MimeConstants.MIME_JPEG.equals(info.getMimeType())) { throw new IllegalArgumentException( "ImageInfo must be from a image with MIME type: " + MimeConstants.MIME_JPEG); } ColorSpace colorSpace = null; boolean appeFound = false; int sofType = 0; ByteArrayOutputStream iccStream = null; final Source src = session.needSource(info.getOriginalURI()); final ImageInputStream in = ImageUtil.needImageInputStream(src); final JPEGFile jpeg = new JPEGFile(in); in.mark(); try { outer: while (true) { int reclen; final int segID = jpeg.readMarkerSegment(); if (log.isTraceEnabled()) { log.trace("Seg Marker: " + Integer.toHexString(segID)); } switch (segID) { case EOI: log.trace("EOI found. Stopping."); break outer; case SOS: log.trace("SOS found. Stopping early."); // TODO Not sure if // this is safe break outer; case SOI: case NULL: break; case SOF0: // baseline case SOF1: // extended sequential DCT case SOF2: // progressive (since PDF 1.3) case SOFA: // progressive (since PDF 1.3) sofType = segID; if (log.isTraceEnabled()) { log.trace("SOF: " + Integer.toHexString(sofType)); } in.mark(); try { reclen = jpeg.readSegmentLength(); in.skipBytes(1); // data precision in.skipBytes(2); // height in.skipBytes(2); // width final int numComponents = in.readUnsignedByte(); if (numComponents == 1) { colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); } else if (numComponents == 3) { colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); } else if (numComponents == 4) { colorSpace = ColorSpaces.getDeviceCMYKColorSpace(); } else { throw new ImageException( "Unsupported ColorSpace for image " + info + ". The number of components supported are 1, 3 and 4."); } } finally { in.reset(); } in.skipBytes(reclen); break; case APP2: // ICC (see ICC1V42.pdf) in.mark(); try { reclen = jpeg.readSegmentLength(); // Check for ICC profile final byte[] iccString = new byte[11]; in.readFully(iccString); in.skipBytes(1); // string terminator (null byte) if ("ICC_PROFILE".equals(new String(iccString, "US-ASCII"))) { in.skipBytes(2); // chunk sequence number and total // number of chunks final int payloadSize = reclen - 2 - 12 - 2; if (ignoreColorProfile(hints)) { log.debug("Ignoring ICC profile data in JPEG"); in.skipBytes(payloadSize); } else { final byte[] buf = new byte[payloadSize]; in.readFully(buf); if (iccStream == null) { if (log.isDebugEnabled()) { log.debug("JPEG has an ICC profile"); final DataInputStream din = new DataInputStream(new ByteArrayInputStream(buf)); log.debug("Declared ICC profile size: " + din.readInt()); } // ICC profiles can be split into several // chunks // so collect in a byte array output stream iccStream = new ByteArrayOutputStream(); } iccStream.write(buf); } } } finally { in.reset(); } in.skipBytes(reclen); break; case APPE: // Adobe-specific (see 5116.DCT_Filter.pdf) in.mark(); try { reclen = jpeg.readSegmentLength(); // Check for Adobe header final byte[] adobeHeader = new byte[5]; in.readFully(adobeHeader); if ("Adobe".equals(new String(adobeHeader, "US-ASCII"))) { // The reason for reading the APPE marker is that // Adobe Photoshop // generates CMYK JPEGs with inverted values. The // correct thing // to do would be to interpret the values in the // marker, but for now // only assume that if APPE marker is present and // colorspace is CMYK, // the image is inverted. appeFound = true; } } finally { in.reset(); } in.skipBytes(reclen); break; default: jpeg.skipCurrentMarkerSegment(); } } } finally { in.reset(); } final ICC_Profile iccProfile = buildICCProfile(info, colorSpace, iccStream); if (iccProfile == null && colorSpace == null) { throw new ImageException("ColorSpace could not be identified for JPEG image " + info); } boolean invertImage = false; if (appeFound && colorSpace.getType() == ColorSpace.TYPE_CMYK) { if (log.isDebugEnabled()) { log.debug( "JPEG has an Adobe APPE marker. Note: CMYK Image will be inverted. (" + info.getOriginalURI() + ")"); } invertImage = true; } final ImageRawJPEG rawImage = new ImageRawJPEG( info, ImageUtil.needInputStream(src), sofType, colorSpace, iccProfile, invertImage); return rawImage; }