aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
diff options
context:
space:
mode:
authorJeremias Maerki <jeremias@apache.org>2008-07-22 15:48:39 +0000
committerJeremias Maerki <jeremias@apache.org>2008-07-22 15:48:39 +0000
commit8091bd11210ac68aa3289532643e42b66545a495 (patch)
tree0c500f554120284fef9debb77c463d738804ff3b /src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
parentcd64af07c5661e82a7121a2ac6ad756a3b2e91e9 (diff)
downloadxmlgraphics-fop-8091bd11210ac68aa3289532643e42b66545a495.tar.gz
xmlgraphics-fop-8091bd11210ac68aa3289532643e42b66545a495.zip
Started the IFParser.
Started a PDF painter. Factored out common code to PDFRenderingUtil. Smaller infrastructure changes for the new IF (like MIME type reporting). git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AreaTreeNewDesign@678780 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java')
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java439
1 files changed, 439 insertions, 0 deletions
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
new file mode 100644
index 000000000..adc3ff771
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
@@ -0,0 +1,439 @@
+/*
+ * 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.Color;
+import java.awt.color.ICC_Profile;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Map;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.xmp.Metadata;
+import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter;
+import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fo.extensions.xmp.XMPMetadata;
+import org.apache.fop.pdf.PDFAMode;
+import org.apache.fop.pdf.PDFColor;
+import org.apache.fop.pdf.PDFConformanceException;
+import org.apache.fop.pdf.PDFDictionary;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFEncryptionManager;
+import org.apache.fop.pdf.PDFEncryptionParams;
+import org.apache.fop.pdf.PDFICCBasedColorSpace;
+import org.apache.fop.pdf.PDFICCStream;
+import org.apache.fop.pdf.PDFInfo;
+import org.apache.fop.pdf.PDFMetadata;
+import org.apache.fop.pdf.PDFNumsArray;
+import org.apache.fop.pdf.PDFOutputIntent;
+import org.apache.fop.pdf.PDFPageLabels;
+import org.apache.fop.pdf.PDFStream;
+import org.apache.fop.pdf.PDFXMode;
+import org.apache.fop.util.ColorProfileUtil;
+
+/**
+ * Utility class which enables all sorts of features that are not directly connected to the
+ * normal rendering process.
+ */
+class PDFRenderingUtil implements PDFConfigurationConstants {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(PDFRenderingUtil.class);
+
+ private FOUserAgent userAgent;
+
+ /** the PDF Document being created */
+ protected PDFDocument pdfDoc;
+
+ /** the PDF/A mode (Default: disabled) */
+ protected PDFAMode pdfAMode = PDFAMode.DISABLED;
+
+ /** the PDF/X mode (Default: disabled) */
+ protected PDFXMode pdfXMode = PDFXMode.DISABLED;
+
+ /** the (optional) encryption parameters */
+ protected PDFEncryptionParams encryptionParams;
+
+ /** Registry of PDF filters */
+ protected Map filterMap;
+
+ /** the ICC stream used as output profile by this document for PDF/A and PDF/X functionality. */
+ protected PDFICCStream outputProfile;
+ /** the default sRGB color space. */
+ protected PDFICCBasedColorSpace sRGBColorSpace;
+ /** controls whether the sRGB color space should be installed */
+ protected boolean disableSRGBColorSpace = false;
+
+ /** Optional URI to an output profile to be used. */
+ protected String outputProfileURI;
+
+
+ PDFRenderingUtil(FOUserAgent userAgent) {
+ this.userAgent = userAgent;
+ initialize();
+ }
+
+ private static boolean booleanValueOf(Object obj) {
+ if (obj instanceof Boolean) {
+ return ((Boolean)obj).booleanValue();
+ } else if (obj instanceof String) {
+ return Boolean.valueOf((String)obj).booleanValue();
+ } else {
+ throw new IllegalArgumentException("Boolean or \"true\" or \"false\" expected.");
+ }
+ }
+
+ private void initialize() {
+ PDFEncryptionParams params
+ = (PDFEncryptionParams)userAgent.getRendererOptions().get(ENCRYPTION_PARAMS);
+ if (params != null) {
+ this.encryptionParams = params; //overwrite if available
+ }
+ String pwd;
+ pwd = (String)userAgent.getRendererOptions().get(USER_PASSWORD);
+ if (pwd != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setUserPassword(pwd);
+ }
+ pwd = (String)userAgent.getRendererOptions().get(OWNER_PASSWORD);
+ if (pwd != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setOwnerPassword(pwd);
+ }
+ Object setting;
+ setting = userAgent.getRendererOptions().get(NO_PRINT);
+ if (setting != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setAllowPrint(!booleanValueOf(setting));
+ }
+ setting = userAgent.getRendererOptions().get(NO_COPY_CONTENT);
+ if (setting != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setAllowCopyContent(!booleanValueOf(setting));
+ }
+ setting = userAgent.getRendererOptions().get(NO_EDIT_CONTENT);
+ if (setting != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setAllowEditContent(!booleanValueOf(setting));
+ }
+ setting = userAgent.getRendererOptions().get(NO_ANNOTATIONS);
+ if (setting != null) {
+ if (encryptionParams == null) {
+ this.encryptionParams = new PDFEncryptionParams();
+ }
+ this.encryptionParams.setAllowEditAnnotations(!booleanValueOf(setting));
+ }
+ String s = (String)userAgent.getRendererOptions().get(PDF_A_MODE);
+ if (s != null) {
+ this.pdfAMode = PDFAMode.valueOf(s);
+ }
+ s = (String)userAgent.getRendererOptions().get(PDF_X_MODE);
+ if (s != null) {
+ this.pdfXMode = PDFXMode.valueOf(s);
+ }
+ s = (String)userAgent.getRendererOptions().get(KEY_OUTPUT_PROFILE);
+ if (s != null) {
+ this.outputProfileURI = s;
+ }
+ setting = userAgent.getRendererOptions().get(KEY_DISABLE_SRGB_COLORSPACE);
+ if (setting != null) {
+ this.disableSRGBColorSpace = booleanValueOf(setting);
+ }
+ }
+
+ public FOUserAgent getUserAgent() {
+ return this.userAgent;
+ }
+
+ /**
+ * Sets the PDF/A mode for the PDF renderer.
+ * @param mode the PDF/A mode
+ */
+ public void setAMode(PDFAMode mode) {
+ this.pdfAMode = mode;
+ }
+
+ /**
+ * Sets the PDF/X mode for the PDF renderer.
+ * @param mode the PDF/X mode
+ */
+ public void setXMode(PDFXMode mode) {
+ this.pdfXMode = mode;
+ }
+
+ /**
+ * Sets the output color profile for the PDF renderer.
+ * @param outputProfileURI the URI to the output color profile
+ */
+ public void setOutputProfileURI(String outputProfileURI) {
+ this.outputProfileURI = outputProfileURI;
+ }
+
+ /**
+ * Enables or disables the default sRGB color space needed for the PDF document to preserve
+ * the sRGB colors used in XSL-FO.
+ * @param disable true to disable, false to enable
+ */
+ public void setDisableSRGBColorSpace(boolean disable) {
+ this.disableSRGBColorSpace = disable;
+ }
+
+ /**
+ * Sets the filter map to be used by the PDF renderer.
+ * @param filterMap the filter map
+ */
+ public void setFilterMap(Map filterMap) {
+ this.filterMap = filterMap;
+ }
+
+ /**
+ * Sets the encryption parameters used by the PDF renderer.
+ * @param encryptionParams the encryption parameters
+ */
+ public void setEncryptionParams(PDFEncryptionParams encryptionParams) {
+ this.encryptionParams = encryptionParams;
+ }
+
+ private void updateInfo() {
+ PDFInfo info = pdfDoc.getInfo();
+ info.setCreator(userAgent.getCreator());
+ info.setCreationDate(userAgent.getCreationDate());
+ info.setAuthor(userAgent.getAuthor());
+ info.setTitle(userAgent.getTitle());
+ info.setKeywords(userAgent.getKeywords());
+ }
+
+ private void updatePDFProfiles() {
+ pdfDoc.getProfile().setPDFAMode(this.pdfAMode);
+ pdfDoc.getProfile().setPDFXMode(this.pdfXMode);
+ }
+
+ private void addsRGBColorSpace() throws IOException {
+ if (disableSRGBColorSpace) {
+ if (this.pdfAMode != PDFAMode.DISABLED
+ || this.pdfXMode != PDFXMode.DISABLED
+ || this.outputProfileURI != null) {
+ throw new IllegalStateException("It is not possible to disable the sRGB color"
+ + " space if PDF/A or PDF/X functionality is enabled or an"
+ + " output profile is set!");
+ }
+ } else {
+ if (this.sRGBColorSpace != null) {
+ return;
+ }
+ //Map sRGB as default RGB profile for DeviceRGB
+ this.sRGBColorSpace = PDFICCBasedColorSpace.setupsRGBAsDefaultRGBColorSpace(pdfDoc);
+ }
+ }
+
+ private void addDefaultOutputProfile() throws IOException {
+ if (this.outputProfile != null) {
+ return;
+ }
+ ICC_Profile profile;
+ InputStream in = null;
+ if (this.outputProfileURI != null) {
+ this.outputProfile = pdfDoc.getFactory().makePDFICCStream();
+ Source src = getUserAgent().resolveURI(this.outputProfileURI);
+ if (src == null) {
+ throw new IOException("Output profile not found: " + this.outputProfileURI);
+ }
+ if (src instanceof StreamSource) {
+ in = ((StreamSource)src).getInputStream();
+ } else {
+ in = new URL(src.getSystemId()).openStream();
+ }
+ try {
+ profile = ICC_Profile.getInstance(in);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ this.outputProfile.setColorSpace(profile, null);
+ } else {
+ //Fall back to sRGB profile
+ outputProfile = sRGBColorSpace.getICCStream();
+ }
+ }
+
+ /**
+ * Adds an OutputIntent to the PDF as mandated by PDF/A-1 when uncalibrated color spaces
+ * are used (which is true if we use DeviceRGB to represent sRGB colors).
+ * @throws IOException in case of an I/O problem
+ */
+ private void addPDFA1OutputIntent() throws IOException {
+ addDefaultOutputProfile();
+
+ String desc = ColorProfileUtil.getICCProfileDescription(this.outputProfile.getICCProfile());
+ PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent();
+ outputIntent.setSubtype(PDFOutputIntent.GTS_PDFA1);
+ outputIntent.setDestOutputProfile(this.outputProfile);
+ outputIntent.setOutputConditionIdentifier(desc);
+ outputIntent.setInfo(outputIntent.getOutputConditionIdentifier());
+ pdfDoc.getRoot().addOutputIntent(outputIntent);
+ }
+
+ /**
+ * Adds an OutputIntent to the PDF as mandated by PDF/X when uncalibrated color spaces
+ * are used (which is true if we use DeviceRGB to represent sRGB colors).
+ * @throws IOException in case of an I/O problem
+ */
+ private void addPDFXOutputIntent() throws IOException {
+ addDefaultOutputProfile();
+
+ String desc = ColorProfileUtil.getICCProfileDescription(this.outputProfile.getICCProfile());
+ int deviceClass = this.outputProfile.getICCProfile().getProfileClass();
+ if (deviceClass != ICC_Profile.CLASS_OUTPUT) {
+ throw new PDFConformanceException(pdfDoc.getProfile().getPDFXMode() + " requires that"
+ + " the DestOutputProfile be an Output Device Profile. "
+ + desc + " does not match that requirement.");
+ }
+ PDFOutputIntent outputIntent = pdfDoc.getFactory().makeOutputIntent();
+ outputIntent.setSubtype(PDFOutputIntent.GTS_PDFX);
+ outputIntent.setDestOutputProfile(this.outputProfile);
+ outputIntent.setOutputConditionIdentifier(desc);
+ outputIntent.setInfo(outputIntent.getOutputConditionIdentifier());
+ pdfDoc.getRoot().addOutputIntent(outputIntent);
+ }
+
+ public void renderXMPMetadata(XMPMetadata metadata) {
+ Metadata docXMP = metadata.getMetadata();
+ Metadata fopXMP = PDFMetadata.createXMPFromPDFDocument(pdfDoc);
+ //Merge FOP's own metadata into the one from the XSL-FO document
+ fopXMP.mergeInto(docXMP);
+ XMPBasicAdapter xmpBasic = XMPBasicSchema.getAdapter(docXMP);
+ //Metadata was changed so update metadata date
+ xmpBasic.setMetadataDate(new java.util.Date());
+ PDFMetadata.updateInfoFromMetadata(docXMP, pdfDoc.getInfo());
+
+ PDFMetadata pdfMetadata = pdfDoc.getFactory().makeMetadata(
+ docXMP, metadata.isReadOnly());
+ pdfDoc.getRoot().setMetadata(pdfMetadata);
+ }
+
+ public void generateDefaultXMPMetadata() {
+ if (pdfDoc.getRoot().getMetadata() == null) {
+ //If at this time no XMP metadata for the overall document has been set, create it
+ //from the PDFInfo object.
+ Metadata xmp = PDFMetadata.createXMPFromPDFDocument(pdfDoc);
+ PDFMetadata pdfMetadata = pdfDoc.getFactory().makeMetadata(
+ xmp, true);
+ pdfDoc.getRoot().setMetadata(pdfMetadata);
+ }
+ }
+
+ public PDFDocument setupPDFDocument(OutputStream out) throws IOException {
+ if (this.pdfDoc != null) {
+ throw new IllegalStateException("PDFDocument already set up");
+ }
+ this.pdfDoc = new PDFDocument(
+ userAgent.getProducer() != null ? userAgent.getProducer() : "");
+ updateInfo();
+ updatePDFProfiles();
+ pdfDoc.setFilterMap(filterMap);
+ pdfDoc.outputHeader(out);
+
+ //Setup encryption if necessary
+ PDFEncryptionManager.setupPDFEncryption(encryptionParams, pdfDoc);
+
+ addsRGBColorSpace();
+ if (this.outputProfileURI != null) {
+ addDefaultOutputProfile();
+ }
+ if (pdfXMode != PDFXMode.DISABLED) {
+ log.debug(pdfXMode + " is active.");
+ log.warn("Note: " + pdfXMode
+ + " support is work-in-progress and not fully implemented, yet!");
+ addPDFXOutputIntent();
+ }
+ if (pdfAMode.isPDFA1LevelB()) {
+ log.debug("PDF/A is active. Conformance Level: " + pdfAMode);
+ addPDFA1OutputIntent();
+ }
+ return this.pdfDoc;
+ }
+
+ /**
+ * Generates a page label in the PDF document.
+ * @param pageIndex the index of the page
+ * @param pageNumber the formatted page number
+ */
+ public void generatePageLabel(int pageIndex, String pageNumber) {
+ //Produce page labels
+ PDFPageLabels pageLabels = this.pdfDoc.getRoot().getPageLabels();
+ if (pageLabels == null) {
+ //Set up PageLabels
+ pageLabels = this.pdfDoc.getFactory().makePageLabels();
+ this.pdfDoc.getRoot().setPageLabels(pageLabels);
+ }
+ PDFNumsArray nums = pageLabels.getNums();
+ PDFDictionary dict = new PDFDictionary(nums);
+ dict.put("P", pageNumber);
+ //TODO If the sequence of generated page numbers were inspected, this could be
+ //expressed in a more space-efficient way
+ nums.put(pageIndex, dict);
+ }
+
+ /**
+ * Establishes a new foreground or fill color. In contrast to updateColor
+ * this method does not check the PDFState for optimization possibilities.
+ * @param col the color to apply
+ * @param fill true to set the fill color, false for the foreground color
+ * @param pdf StringBuffer to write the PDF code to
+ */
+ public void setColor(Color col, boolean fill, StringBuffer pdf) {
+ assert pdf != null;
+ PDFColor color = new PDFColor(this.pdfDoc, col);
+ pdf.append(color.getColorSpaceOut(fill));
+ }
+
+ /**
+ * Establishes a new foreground or fill color.
+ * @param col the color to apply
+ * @param fill true to set the fill color, false for the foreground color
+ * @param stream the PDFStream to write the PDF code to
+ */
+ public void setColor(Color col, boolean fill, PDFStream stream) {
+ assert stream != null;
+ PDFColor color = new PDFColor(this.pdfDoc, col);
+ stream.add(color.getColorSpaceOut(fill));
+ }
+
+
+}