123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- /*
- * 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.render.pdf;
-
- import java.awt.Dimension;
- import java.awt.Rectangle;
- import java.awt.geom.AffineTransform;
- import java.awt.geom.Point2D;
- import java.awt.geom.Rectangle2D;
- import java.io.BufferedOutputStream;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.URI;
- import java.util.HashMap;
- import java.util.Locale;
- import java.util.Map;
-
- import org.apache.commons.io.output.CountingOutputStream;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.xmlgraphics.io.TempResourceURIGenerator;
- import org.apache.xmlgraphics.xmp.Metadata;
-
- import org.apache.fop.accessibility.StructureTreeEventHandler;
- import org.apache.fop.apps.MimeConstants;
- import org.apache.fop.fo.extensions.xmp.XMPMetadata;
- import org.apache.fop.pdf.PDFAnnotList;
- import org.apache.fop.pdf.PDFArray;
- import org.apache.fop.pdf.PDFDocument;
- import org.apache.fop.pdf.PDFPage;
- import org.apache.fop.pdf.PDFReference;
- import org.apache.fop.pdf.PDFResources;
- import org.apache.fop.pdf.PDFSignature;
- import org.apache.fop.pdf.PDFStream;
- import org.apache.fop.render.extensions.prepress.PageBoundaries;
- import org.apache.fop.render.extensions.prepress.PageScale;
- import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
- import org.apache.fop.render.intermediate.IFContext;
- import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
- import org.apache.fop.render.intermediate.IFDocumentNavigationHandler;
- import org.apache.fop.render.intermediate.IFException;
- import org.apache.fop.render.intermediate.IFPainter;
- import org.apache.fop.render.pdf.PDFRendererConfig.PDFRendererConfigParser;
- import org.apache.fop.render.pdf.extensions.PDFDictionaryAttachment;
- import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileAttachment;
-
- /**
- * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation that produces PDF.
- */
- public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
-
- /** logging instance */
- private static Log log = LogFactory.getLog(PDFDocumentHandler.class);
- private static final TempResourceURIGenerator SIGN_TEMP_URI_GENERATOR = new TempResourceURIGenerator("pdfsign");
-
- private boolean accessEnabled;
-
- private PDFLogicalStructureHandler logicalStructureHandler;
-
- private PDFStructureTreeBuilder structureTreeBuilder;
-
- /** the PDF Document being created */
- private PDFDocument pdfDoc;
-
- /**
- * Utility class which enables all sorts of features that are not directly connected to the
- * normal rendering process.
- */
- private final PDFRenderingUtil pdfUtil;
-
- /** the /Resources object of the PDF document being created */
- private PDFResources pdfResources;
-
- /** The current content generator */
- private PDFContentGenerator generator;
-
- /** the current page to add annotations to */
- private PDFPage currentPage;
-
- /** the current page's PDF reference */
- private PageReference currentPageRef;
-
- /** Used for bookmarks/outlines. */
- private Map<Integer, PageReference> pageReferences = new HashMap<Integer, PageReference>();
-
- private final PDFDocumentNavigationHandler documentNavigationHandler
- = new PDFDocumentNavigationHandler(this);
-
- private Map<String, Object> usedFieldNames = new HashMap<>();
- private Map<Integer, PDFArray> pageNumbers = new HashMap<Integer, PDFArray>();
- private Map<String, PDFReference> contents = new HashMap<String, PDFReference>();
- private PDFSignature pdfSignature;
- private URI signTempURI;
- private OutputStream orgOutputStream;
-
- /**
- * Default constructor.
- */
- public PDFDocumentHandler(IFContext context) {
- super(context);
- this.pdfUtil = new PDFRenderingUtil(context.getUserAgent());
- }
-
- /** {@inheritDoc} */
- public boolean supportsPagesOutOfOrder() {
- return !accessEnabled;
- }
-
- /** {@inheritDoc} */
- public String getMimeType() {
- return MimeConstants.MIME_PDF;
- }
-
- /** {@inheritDoc} */
- public IFDocumentHandlerConfigurator getConfigurator() {
- return new PDFRendererConfigurator(getUserAgent(), new PDFRendererConfigParser());
- }
-
- /** {@inheritDoc} */
- public IFDocumentNavigationHandler getDocumentNavigationHandler() {
- return this.documentNavigationHandler;
- }
-
- void mergeRendererOptionsConfig(PDFRendererOptionsConfig config) {
- pdfUtil.mergeRendererOptionsConfig(config);
- }
-
- PDFLogicalStructureHandler getLogicalStructureHandler() {
- return logicalStructureHandler;
- }
-
- PDFDocument getPDFDocument() {
- return pdfDoc;
- }
-
- PDFPage getCurrentPage() {
- return currentPage;
- }
-
- PageReference getCurrentPageRef() {
- return currentPageRef;
- }
-
- PDFContentGenerator getGenerator() {
- return generator;
- }
-
- /** {@inheritDoc} */
- public void startDocument() throws IFException {
- super.startDocument();
- try {
- setupPDFSigning();
- this.pdfDoc = pdfUtil.setupPDFDocument(outputStream);
- this.accessEnabled = getUserAgent().isAccessibilityEnabled();
- if (accessEnabled) {
- setupAccessibility();
- }
- } catch (IOException e) {
- throw new IFException("I/O error in startDocument()", e);
- }
- }
-
- private void setupPDFSigning() throws IOException {
- if (pdfUtil.getSignParams() != null) {
- orgOutputStream = outputStream;
- signTempURI = SIGN_TEMP_URI_GENERATOR.generate();
- outputStream = new BufferedOutputStream(getUserAgent().getResourceResolver().getOutputStream(signTempURI));
- outputStream = new CountingOutputStream(outputStream);
- }
- }
-
- private void setupAccessibility() {
- pdfDoc.getRoot().makeTagged();
- logicalStructureHandler = new PDFLogicalStructureHandler(pdfDoc);
- // TODO this is ugly. All the necessary information should be available
- // at creation time in order to enforce immutability
- structureTreeBuilder.setPdfFactory(pdfDoc.getFactory());
- structureTreeBuilder.setLogicalStructureHandler(logicalStructureHandler);
- structureTreeBuilder.setEventBroadcaster(getUserAgent().getEventBroadcaster());
- }
-
- /** {@inheritDoc} */
- public void endDocumentHeader() throws IFException {
- pdfUtil.generateDefaultXMPMetadata();
- }
-
- /** {@inheritDoc} */
- public void endDocument() throws IFException {
- documentNavigationHandler.registerIncompleteActions();
- pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
- try {
- if (pdfDoc.isLinearizationEnabled()) {
- generator.flushPDFDoc();
- } else {
- pdfDoc.outputTrailer(this.outputStream);
- }
- this.pdfDoc = null;
-
- pdfResources = null;
- this.generator = null;
- currentPage = null;
- } catch (IOException ioe) {
- throw new IFException("I/O error in endDocument()", ioe);
- }
- super.endDocument();
- signPDF();
- }
-
- private void signPDF() {
- if (signTempURI != null) {
- try {
- outputStream.close();
- pdfSignature.signPDF(signTempURI, orgOutputStream);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- /** {@inheritDoc} */
- public void startPageSequence(String id) throws IFException {
- //nop
- }
-
- /** {@inheritDoc} */
- public void endPageSequence() throws IFException {
- //nop
- }
-
- /** {@inheritDoc} */
- public void startPage(int index, String name, String pageMasterName, Dimension size)
- throws IFException {
- this.pdfResources = this.pdfDoc.getResources();
-
- PageBoundaries boundaries = new PageBoundaries(size, getContext().getForeignAttributes());
-
- Rectangle trimBox = boundaries.getTrimBox();
- Rectangle bleedBox = boundaries.getBleedBox();
- Rectangle mediaBox = boundaries.getMediaBox();
- Rectangle cropBox = boundaries.getCropBox();
-
- // set scale attributes
- double scaleX = 1;
- double scaleY = 1;
- String scale = (String) getContext().getForeignAttribute(
- PageScale.EXT_PAGE_SCALE);
- Point2D scales = PageScale.getScale(scale);
- if (scales != null) {
- scaleX = scales.getX();
- scaleY = scales.getY();
- }
-
- //PDF uses the lower left as origin, need to transform from FOP's internal coord system
- AffineTransform boxTransform = new AffineTransform(
- scaleX / 1000, 0, 0, -scaleY / 1000, 0, scaleY * size.getHeight() / 1000);
-
- this.currentPage = this.pdfDoc.getFactory().makePage(
- this.pdfResources,
- index,
- toPDFCoordSystem(mediaBox, boxTransform),
- toPDFCoordSystem(cropBox, boxTransform),
- toPDFCoordSystem(bleedBox, boxTransform),
- toPDFCoordSystem(trimBox, boxTransform));
- if (pdfDoc.getProfile().isPDFVTActive()) {
- pdfDoc.getFactory().makeDPart(currentPage, pageMasterName);
- }
- if (accessEnabled) {
- logicalStructureHandler.startPage(currentPage);
- }
-
- pdfUtil.generatePageLabel(index, name);
-
- currentPageRef = new PageReference(currentPage, size);
- this.pageReferences.put(index, currentPageRef);
-
- this.generator = new PDFContentGenerator(this.pdfDoc, this.outputStream, this.currentPage, getContext());
- // Transform the PDF's default coordinate system (0,0 at lower left) to the PDFPainter's
- AffineTransform basicPageTransform = new AffineTransform(1, 0, 0, -1, 0,
- (scaleY * size.height) / 1000f);
- basicPageTransform.scale(scaleX, scaleY);
- generator.saveGraphicsState();
- generator.concatenate(basicPageTransform);
-
- if (signTempURI != null && pdfSignature == null) {
- pdfSignature = new PDFSignature(pdfDoc.getRoot(), getUserAgent(), pdfUtil.getSignParams());
- pdfSignature.add(currentPage);
- }
- }
-
- private Rectangle2D toPDFCoordSystem(Rectangle box, AffineTransform transform) {
- return transform.createTransformedShape(box).getBounds2D();
- }
-
- /** {@inheritDoc} */
- public IFPainter startPageContent() throws IFException {
- return new PDFPainter(this, logicalStructureHandler);
- }
-
- /** {@inheritDoc} */
- public void endPageContent() throws IFException {
- generator.restoreGraphicsState();
- //for top-level transform to change the default coordinate system
- }
-
- /** {@inheritDoc} */
- public void endPage() throws IFException {
- if (accessEnabled) {
- logicalStructureHandler.endPage();
- }
- try {
- this.documentNavigationHandler.commit();
- setUpContents();
- PDFAnnotList annots = currentPage.getAnnotations();
- if (annots != null) {
- this.pdfDoc.addObject(annots);
- }
- this.pdfDoc.addObject(currentPage);
-
- if (!pdfDoc.isLinearizationEnabled()) {
- this.generator.flushPDFDoc();
- this.generator = null;
- }
- } catch (IOException ioe) {
- throw new IFException("I/O error in endPage()", ioe);
- }
- }
-
- private void setUpContents() throws IOException {
- PDFStream stream = generator.getStream();
- String hash = stream.streamHashCode();
- if (!contents.containsKey(hash)) {
- pdfDoc.registerObject(stream);
- PDFReference ref = new PDFReference(stream);
- contents.put(hash, ref);
- }
- currentPage.setContents(contents.get(hash));
- }
-
- /** {@inheritDoc} */
- public void handleExtensionObject(Object extension) throws IFException {
- if (extension instanceof XMPMetadata) {
- pdfUtil.renderXMPMetadata((XMPMetadata) extension);
- } else if (extension instanceof Metadata) {
- XMPMetadata wrapper = new XMPMetadata(((Metadata) extension));
- pdfUtil.renderXMPMetadata(wrapper);
- } else if (extension instanceof PDFEmbeddedFileAttachment) {
- PDFEmbeddedFileAttachment embeddedFile
- = (PDFEmbeddedFileAttachment)extension;
- try {
- pdfUtil.addEmbeddedFile(embeddedFile);
- } catch (IOException ioe) {
- throw new IFException("Error adding embedded file: " + embeddedFile.getSrc(), ioe);
- }
- } else if (extension instanceof PDFDictionaryAttachment) {
- pdfUtil.renderDictionaryExtension((PDFDictionaryAttachment) extension, currentPage);
- } else if (extension != null) {
- log.debug("Don't know how to handle extension object. Ignoring: "
- + extension + " (" + extension.getClass().getName() + ")");
- } else {
- log.debug("Ignoring null extension object.");
- }
- }
-
- /** {@inheritDoc} */
- public void setDocumentLocale(Locale locale) {
- pdfDoc.getRoot().setLanguage(locale);
- }
-
- PageReference getPageReference(int pageIndex) {
- return this.pageReferences.get(pageIndex);
- }
-
- static final class PageReference {
-
- private final PDFReference pageRef;
- private final Dimension pageDimension;
-
- private PageReference(PDFPage page, Dimension dim) {
- this.pageRef = page.makeReference();
- this.pageDimension = new Dimension(dim);
- }
-
- public PDFReference getPageRef() {
- return this.pageRef;
- }
-
- public Dimension getPageDimension() {
- return this.pageDimension;
- }
- }
-
- @Override
- public StructureTreeEventHandler getStructureTreeEventHandler() {
- if (structureTreeBuilder == null) {
- structureTreeBuilder = new PDFStructureTreeBuilder();
- }
- return structureTreeBuilder;
- }
-
- public Map<String, Object> getUsedFieldNames() {
- return usedFieldNames;
- }
-
- public Map<Integer, PDFArray> getPageNumbers() {
- return pageNumbers;
- }
- }
|