Java tutorial
/* * 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: PreloaderPDF.java 1740069 2016-04-20 08:51:49Z ssteiner $ */ package org.apache.fop.render.pdf.pdfbox; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; import java.util.Locale; import java.util.Map; import java.util.WeakHashMap; import javax.imageio.stream.ImageInputStream; import javax.xml.transform.Source; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.xmlgraphics.image.loader.ImageContext; import org.apache.xmlgraphics.image.loader.ImageException; import org.apache.xmlgraphics.image.loader.ImageInfo; import org.apache.xmlgraphics.image.loader.ImageSize; import org.apache.xmlgraphics.image.loader.impl.AbstractImagePreloader; import org.apache.xmlgraphics.image.loader.util.ImageUtil; import org.apache.xmlgraphics.util.io.SubInputStream; import org.apache.fop.datatypes.URISpecification; import org.apache.fop.render.pdf.pdfbox.Cache.ValueMaker; /** * Image preloader for PDF images. */ public class PreloaderPDF extends AbstractImagePreloader { /** PDF header text */ private static final String PDF_HEADER = "%PDF-"; /** static PDDocument cache for faster multi-page processing */ private static final Cache.Type CACHE_TYPE = Cache.Type.valueOf( System.getProperty("fop.pdfbox.preloader-cache", Cache.Type.WEAK.name()).toUpperCase(Locale.ENGLISH)); private static Map<Object, Cache<URI, PDDocument>> documentCacheMap = Collections .synchronizedMap(new WeakHashMap<Object, Cache<URI, PDDocument>>()); //the cache here can cause problems because PDDocument that have been closed might still //be accessed. Example: java.io.IOException: The handle is invalid /** {@inheritDoc} */ public ImageInfo preloadImage(String uri, Source src, ImageContext context) throws IOException, ImageException { if (!ImageUtil.hasImageInputStream(src)) { return null; } ImageInfo info = null; ImageInputStream in = ImageUtil.needImageInputStream(src); in.mark(); try { byte[] header = getHeader(in, PDF_HEADER.length()); String s = new String(header, "US-ASCII"); boolean supported = PDF_HEADER.equals(s); if (supported) { info = loadPDF(uri, src, context); } return info; } finally { if (info == null) { in.reset(); //Error detected or not a PDF file } } } private static URI deriveDocumentURI(String uri) throws ImageException { try { URI originalURI = new URI(URISpecification.escapeURI(uri)); URI tempURI = new URI(originalURI.getScheme(), originalURI.getSchemeSpecificPart(), null); return tempURI; } catch (URISyntaxException e) { throw new ImageException("Problem processing URI", e); } } private ImageInfo loadPDF(String uri, Source src, ImageContext context) throws IOException, ImageException { int selectedPage = ImageUtil.needPageIndexFromURI(uri); URI docURI = deriveDocumentURI(src.getSystemId()); PDDocument pddoc = getDocument(context, docURI, src); pddoc = Interceptors.getInstance().interceptOnLoad(pddoc, docURI); //Disable the warning about a missing close since we rely on the GC to decide when //the cached PDF shall be disposed off. pddoc.getDocument().setWarnMissingClose(false); int pageCount = pddoc.getNumberOfPages(); if (selectedPage < 0 || selectedPage >= pageCount) { throw new ImageException("Selected page (index: " + selectedPage + ") does not exist in the PDF file. The document has " + pddoc.getNumberOfPages() + " pages."); } PDPage page = pddoc.getDocumentCatalog().getPages().get(selectedPage); PDRectangle mediaBox = page.getMediaBox(); PDRectangle cropBox = page.getCropBox(); PDRectangle viewBox = cropBox != null ? cropBox : mediaBox; int w = Math.round(viewBox.getWidth() * 1000); int h = Math.round(viewBox.getHeight() * 1000); //Handle the /Rotation entry on the page dict int rotation = PDFUtil.getNormalizedRotation(page); if (rotation == 90 || rotation == 270) { //Swap width and height int exch = w; w = h; h = exch; } ImageSize size = new ImageSize(); size.setSizeInMillipoints(w, h); size.setResolution(context.getSourceResolution()); size.calcPixelsFromSize(); ImageInfo info = new ImageInfo(uri, ImagePDF.MIME_PDF); info.setSize(size); info.getCustomObjects().put(ImageInfo.ORIGINAL_IMAGE, new ImagePDF(info, pddoc)); int lastPageIndex = pddoc.getNumberOfPages() - 1; if (selectedPage < lastPageIndex) { info.getCustomObjects().put(ImageInfo.HAS_MORE_IMAGES, Boolean.TRUE); } return info; } // private void notifyCouldNotDecrypt(Exception e) // throws ImageException { // throw new ImageException("Error decrypting PDF: " // + e.getMessage() // + "\nPlease use an OnLoadInterceptor to provide " // + "suitable decryption material (ex. a password).", e); // } private PDDocument getDocument(Object context, URI uri, Source src) throws IOException { try { return getDocumentCache(context).getValue(uri, createDocumentMaker(src, uri)); } catch (IOException ioe) { throw ioe; } catch (Exception e) { // We cannot recover from this throw new RuntimeException(e); } } private Cache<URI, PDDocument> getDocumentCache(Object context) { Cache<URI, PDDocument> documentCache = documentCacheMap.get(context); if (documentCache == null) { documentCache = Cache.createCache(CACHE_TYPE); documentCacheMap.put(context, documentCache); } return documentCache; } private ValueMaker<PDDocument> createDocumentMaker(final Source src, final URI docURI) { return new DocumentMaker(src, docURI); } static class DocumentMaker implements ValueMaker<PDDocument> { private Source src; private URI docURI; public DocumentMaker(Source src, URI docURI) { this.src = src; this.docURI = docURI; } public PDDocument make() throws Exception { final InputStream in = ImageUtil.needInputStream(src); try { PDDocument pddoc = PDDocument.load(new SubInputStream(in, Integer.MAX_VALUE)); return Interceptors.getInstance().interceptOnLoad(pddoc, docURI); } finally { ImageUtil.closeQuietly(src); } } }; }