- /*
- * 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.pdf;
-
- import java.io.IOException;
- import java.io.OutputStream;
- import java.util.Date;
-
- import javax.xml.transform.TransformerConfigurationException;
-
- import org.xml.sax.SAXException;
-
- import org.apache.xmlgraphics.xmp.Metadata;
- import org.apache.xmlgraphics.xmp.XMPSerializer;
- import org.apache.xmlgraphics.xmp.schemas.DublinCoreAdapter;
- import org.apache.xmlgraphics.xmp.schemas.DublinCoreSchema;
- import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter;
- import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
- import org.apache.xmlgraphics.xmp.schemas.pdf.AdobePDFAdapter;
- import org.apache.xmlgraphics.xmp.schemas.pdf.AdobePDFSchema;
- import org.apache.xmlgraphics.xmp.schemas.pdf.PDFAAdapter;
- import org.apache.xmlgraphics.xmp.schemas.pdf.PDFAXMPSchema;
-
- /**
- * Special PDFStream for Metadata.
- * @since PDF 1.4
- */
- public class PDFMetadata extends PDFStream {
-
- private Metadata xmpMetadata;
- private boolean readOnly = true;
-
- /**
- * @param xmp xmp metadata
- * @param readOnly true if read only
- * @see org.apache.fop.pdf.PDFObject#PDFObject()
- */
- public PDFMetadata(Metadata xmp, boolean readOnly) {
- super();
- if (xmp == null) {
- throw new NullPointerException(
- "The parameter for the XMP Document must not be null");
- }
- this.xmpMetadata = xmp;
- this.readOnly = readOnly;
- }
-
- /** {@inheritDoc} */
- protected String getDefaultFilterName() {
- return PDFFilterList.METADATA_FILTER;
- }
-
- /**
- * @return the XMP metadata
- */
- public Metadata getMetadata() {
- return this.xmpMetadata;
- }
-
- /**
- * overload the base object method so we don't have to copy
- * byte arrays around so much
- * {@inheritDoc}
- */
- public int output(java.io.OutputStream stream)
- throws java.io.IOException {
- int length = super.output(stream);
- this.xmpMetadata = null; //Release DOM when it's not used anymore
- return length;
- }
-
- /** {@inheritDoc} */
- protected void outputRawStreamData(OutputStream out) throws IOException {
- try {
- XMPSerializer.writeXMPPacket(xmpMetadata, out, this.readOnly);
- } catch (TransformerConfigurationException tce) {
- throw new IOException("Error setting up Transformer for XMP stream serialization: "
- + tce.getMessage());
- } catch (SAXException saxe) {
- throw new IOException("Error while serializing XMP stream: "
- + saxe.getMessage());
- }
- }
-
- /** {@inheritDoc} */
- protected void populateStreamDict(Object lengthEntry) {
- final String filterEntry = getFilterList().buildFilterDictEntries();
- if (getDocumentSafely().getProfile().getPDFAMode().isPDFA1LevelB()
- && filterEntry != null && filterEntry.length() > 0) {
- throw new PDFConformanceException(
- "The Filter key is prohibited when PDF/A-1 is active");
- }
- put("Type", new PDFName("Metadata"));
- put("Subtype", new PDFName("XML"));
- super.populateStreamDict(lengthEntry);
- }
-
- /**
- * Creates an XMP document based on the settings on the PDF Document.
- * @param pdfDoc the PDF Document
- * @return the requested XMP metadata
- */
- public static Metadata createXMPFromPDFDocument(PDFDocument pdfDoc) {
- Metadata meta = new Metadata();
-
- PDFInfo info = pdfDoc.getInfo();
- PDFRoot root = pdfDoc.getRoot();
-
- //Set creation date if not available, yet
- if (info.getCreationDate() == null) {
- Date d = new Date();
- info.setCreationDate(d);
- }
-
- //Important: Acrobat 7's preflight check for PDF/A-1b wants the creation date in the Info
- //object and in the XMP metadata to have the same timezone or else it shows a validation
- //error even if the times are essentially equal.
-
- //Dublin Core
- DublinCoreAdapter dc = DublinCoreSchema.getAdapter(meta);
- if (info.getAuthor() != null) {
- dc.addCreator(info.getAuthor());
- }
- if (info.getTitle() != null) {
- dc.setTitle(info.getTitle());
- }
- if (info.getSubject() != null) {
- //Subject maps to dc:description["x-default"] as per ISO-19005-1:2005/Cor.1:2007
- dc.setDescription(null, info.getSubject());
- }
- if (root.getLanguage() != null) {
- //Note: No check is performed to make sure the value is valid RFC 3066!
- dc.addLanguage(root.getLanguage());
- }
- dc.addDate(info.getCreationDate());
-
- //Somewhat redundant but some PDF/A checkers issue a warning without this.
- dc.setFormat("application/pdf");
-
- //PDF/A identification
- PDFAMode pdfaMode = pdfDoc.getProfile().getPDFAMode();
- if (pdfaMode.isPDFA1LevelB()) {
- PDFAAdapter pdfa = PDFAXMPSchema.getAdapter(meta);
- pdfa.setPart(1);
- if (pdfaMode == PDFAMode.PDFA_1A) {
- pdfa.setConformance("A"); //PDF/A-1a
- } else {
- pdfa.setConformance("B"); //PDF/A-1b
- }
- }
-
- //XMP Basic Schema
- XMPBasicAdapter xmpBasic = XMPBasicSchema.getAdapter(meta);
- xmpBasic.setCreateDate(info.getCreationDate());
- PDFProfile profile = pdfDoc.getProfile();
- if (info.getModDate() != null) {
- xmpBasic.setModifyDate(info.getModDate());
- } else if (profile.isModDateRequired()) {
- //if modify date is needed but none is in the Info object, use creation date
- xmpBasic.setModifyDate(info.getCreationDate());
- }
- if (info.getCreator() != null) {
- xmpBasic.setCreatorTool(info.getCreator());
- }
-
- AdobePDFAdapter adobePDF = AdobePDFSchema.getAdapter(meta);
- if (info.getKeywords() != null) {
- adobePDF.setKeywords(info.getKeywords());
- }
- if (info.getProducer() != null) {
- adobePDF.setProducer(info.getProducer());
- }
- adobePDF.setPDFVersion(pdfDoc.getPDFVersionString());
-
-
- return meta;
- }
-
- /**
- * Updates the values in the Info object from the XMP metadata according to the rules defined
- * in PDF/A-1 (ISO 19005-1:2005)
- * @param meta the metadata
- * @param info the Info object
- */
- public static void updateInfoFromMetadata(Metadata meta, PDFInfo info) {
- DublinCoreAdapter dc = DublinCoreSchema.getAdapter(meta);
- info.setTitle(dc.getTitle());
- String[] creators = dc.getCreators();
- if (creators != null && creators.length > 0) {
- info.setAuthor(creators[0]);
- } else {
- info.setAuthor(null);
- }
-
- //dc:description["x-default"] maps to Subject as per ISO-19005-1:2005/Cor.1:2007
- info.setSubject(dc.getDescription());
-
- AdobePDFAdapter pdf = AdobePDFSchema.getAdapter(meta);
- info.setKeywords(pdf.getKeywords());
- info.setProducer(pdf.getProducer());
-
- XMPBasicAdapter xmpBasic = XMPBasicSchema.getAdapter(meta);
- info.setCreator(xmpBasic.getCreatorTool());
- Date d;
- d = xmpBasic.getCreateDate();
- xmpBasic.setCreateDate(d); //To make Adobe Acrobat happy (bug filed with Adobe)
- //Adobe Acrobat doesn't like it when the xmp:CreateDate has a different timezone
- //than Info/CreationDate
- info.setCreationDate(d);
- d = xmpBasic.getModifyDate();
- if (d != null) { //ModifyDate is only required for PDF/X
- xmpBasic.setModifyDate(d);
- info.setModDate(d);
- }
- }
- }
|