Index: src/java/org/apache/fop/events/EventBroadcaster.java
===================================================================
--- src/java/org/apache/fop/events/EventBroadcaster.java	(revision 1599339)
+++ src/java/org/apache/fop/events/EventBroadcaster.java	(working copy)
@@ -56,6 +56,6 @@
      * @param clazz the Class object identifying an {@link EventProducer} interface
      * @return the event producer instance
      */
-    EventProducer getEventProducerFor(Class clazz);
+    <T extends EventProducer> T getEventProducerFor(Class<T> clazz);
 
 }
Index: src/java/org/apache/fop/fo/FOTreeBuilder.java
===================================================================
--- src/java/org/apache/fop/fo/FOTreeBuilder.java	(revision 1599339)
+++ src/java/org/apache/fop/fo/FOTreeBuilder.java	(working copy)
@@ -40,6 +40,7 @@
 import org.apache.fop.fo.ElementMapping.Maker;
 import org.apache.fop.fo.extensions.ExtensionElementMapping;
 import org.apache.fop.fo.pagination.Root;
+import org.apache.fop.render.pdf.extensions.PDFElementMapping;
 import org.apache.fop.util.ContentHandlerFactory;
 import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
 import org.apache.fop.util.ContentHandlerFactory.ObjectSource;
@@ -270,7 +271,8 @@
                 }
             } else { // check that incoming node is valid for currentFObj
                 if (currentFObj.getNamespaceURI().equals(FOElementMapping.URI)
-                    || currentFObj.getNamespaceURI().equals(ExtensionElementMapping.URI)) {
+                    || currentFObj.getNamespaceURI().equals(ExtensionElementMapping.URI)
+                    || currentFObj.getNamespaceURI().equals(PDFElementMapping.NAMESPACE)) {
                     currentFObj.validateChildNode(locator, namespaceURI, localName);
                 }
             }
Index: src/java/org/apache/fop/pdf/PDFInfo.java
===================================================================
--- src/java/org/apache/fop/pdf/PDFInfo.java	(revision 1599339)
+++ src/java/org/apache/fop/pdf/PDFInfo.java	(working copy)
@@ -22,6 +22,8 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.TimeZone;
 
 import org.apache.xmlgraphics.util.DateFormatUtil;
@@ -31,6 +33,49 @@
  */
 public class PDFInfo extends PDFObject {
 
+    /** The standard keys in the Document Information Dictionary */
+    public static enum StandardKey {
+        TITLE("Title"),
+        AUTHOR("Author"),
+        SUBJECT("Subject"),
+        KEYWORDS("Keywords"),
+        CREATOR("Creator"),
+        PRODUCER("Producer"),
+        CREATION_DATE("CreationDate"),
+        MOD_DATE("ModDate"),
+        TRAPPED("Trapped");
+
+        private final String name;
+
+        private StandardKey(String name) {
+            this.name = name;
+        }
+
+        /**
+         * Returns the standard key that corresponds to the given name.
+         *
+         * @param name key name
+         * @return the key whose name exactly matches the given name (case-sensitive)
+         */
+        public static StandardKey get(String name) {
+            for (StandardKey key : values()) {
+                if (key.name.equals(name)) {
+                    return key;
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Returns the name of this key.
+         *
+         * @return the name as it would appear in the Info dictionary without the leading slash
+         */
+        public String getName() {
+            return name;
+        }
+    }
+
     /**
      * the application producing the PDF
      */
@@ -43,6 +88,8 @@
     private Date creationDate = null;
     private Date modDate = null;
 
+    private Map<PDFName, String> customProperties;
+
     /**
      * the name of the application that created the
      * original document before converting to PDF
@@ -227,6 +274,14 @@
                 bout.write(encode("/Trapped /False\n"));
             }
 
+            if (customProperties != null) {
+                for (Map.Entry<PDFName, String> entry : customProperties.entrySet()) {
+                    entry.getKey().output(bout);
+                    bout.write(encode(" "));
+                    bout.write(encodeText(entry.getValue()));
+                    bout.write(encode("\n"));
+                }
+            }
             bout.write(encode(">>"));
         } catch (IOException ioe) {
             log.error("Ignored I/O exception", ioe);
@@ -252,5 +307,20 @@
     protected static String formatDateTime(final Date time) {
         return formatDateTime(time, TimeZone.getDefault());
     }
+
+    /**
+     * Adds a custom property to this Info dictionary.
+     */
+    public void put(String key, String value) {
+        StandardKey standardKey = StandardKey.get(key);
+        if (standardKey != null) {
+            throw new IllegalArgumentException(key + " is a reserved keyword");
+        }
+        if (customProperties == null) {
+            customProperties = new HashMap<PDFName, String>();
+        }
+        customProperties.put(new PDFName(key), value);
+    }
+
 }
 
Index: src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
===================================================================
--- src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java	(revision 1599339)
+++ src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java	(working copy)
@@ -455,6 +455,11 @@
             if (((PDFPageExtension) extension).matchesPageNumber(currentPage.getPageIndex() + 1)) {
                 augmentDictionary(currentPage, extension);
             }
+        } else if (type == PDFDictionaryType.Info) {
+            PDFInfo info = pdfDoc.getInfo();
+            for (PDFCollectionEntryExtension entry : extension.getEntries()) {
+                info.put(entry.getKey(), entry.getValueAsString());
+            }
         } else {
             throw new IllegalStateException();
         }
Index: src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java
===================================================================
--- src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java	(revision 1599339)
+++ src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java	(working copy)
@@ -56,6 +56,8 @@
             return new PDFNavigatorExtension();
         } else if (type == PDFDictionaryType.Page) {
             return new PDFPageExtension();
+        } else if (type == PDFDictionaryType.Info) {
+            return new PDFDocumentInformationExtension();
         } else {
             return new PDFDictionaryExtension(type);
         }
@@ -103,6 +105,8 @@
             // handled in PDFNavigattorElement subclass
         } else if (localName.equals("page")) {
             // handled in PDFPageElement subclass
+        } else if (localName.equals("info")) {
+            // handled in PDFDocumentInformationElement subclass
         } else if (localName.equals("dictionary")) {
             if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName()) && !PDFObjectType.Array.elementName().equals(parent.getLocalName())) {
                 invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null);
Index: src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java
===================================================================
--- src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java	(revision 1599339)
+++ src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java	(working copy)
@@ -30,7 +30,9 @@
     Dictionary("dictionary"),           // generic (nested) dictionary element
     Layer("layer", true),               // optional content group dictionary element
     Navigator("navigator", true),       // navigation node dictionary element
-    Page("page");                       // page dictionary element
+    Page("page"),                       // page dictionary element
+    /** Document Information Dictionary */
+    Info("info");
 
     private String elementName;
     private boolean usesIDAttribute;
Index: src/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationElement.java
===================================================================
--- src/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationElement.java	(revision 0)
+++ src/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationElement.java	(working copy)
@@ -0,0 +1,75 @@
+/*
+ * 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.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.ValidationException;
+import org.apache.fop.pdf.PDFInfo;
+
+public class PDFDocumentInformationElement extends PDFDictionaryElement {
+
+    PDFDocumentInformationElement(FONode parent) {
+        super(parent, PDFDictionaryType.Info);
+    }
+
+    @Override
+    public void processNode(String elementName, Locator locator, Attributes attlist,
+            PropertyList propertyList) throws FOPException {
+         setLocator(locator);
+         super.processNode(elementName, locator, attlist, propertyList);
+    }
+
+    @Override
+    public void startOfNode() throws FOPException {
+        super.startOfNode();
+        if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+            invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations");
+        }
+    }
+
+    @Override
+    protected void validateChildNode(Locator loc, String namespaceURI, String localName)
+            throws ValidationException {
+        if (!(PDFElementMapping.NAMESPACE.equals(namespaceURI) && "name".equals(localName))) {
+            invalidChildError(loc, namespaceURI, localName);
+        }
+    }
+
+    @Override
+    protected void addChildNode(FONode child) throws FOPException {
+        assert child instanceof PDFCollectionEntryElement;
+        PDFCollectionEntryElement name = (PDFCollectionEntryElement) child;
+        PDFInfo.StandardKey standardKey = PDFInfo.StandardKey.get(name.getExtension().getKey());
+        if (standardKey == null) {
+            super.addChildNode(child);
+        } else {
+            PDFExtensionEventProducer eventProducer = getUserAgent().getEventBroadcaster()
+                    .getEventProducerFor(PDFExtensionEventProducer.class);
+            eventProducer.reservedKeyword(this, getLocator(), standardKey.getName());
+        }
+    }
+
+}

Property changes on: src/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationElement.java
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Revision Id
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: src/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationExtension.java
===================================================================
--- src/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationExtension.java	(revision 0)
+++ src/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationExtension.java	(working copy)
@@ -0,0 +1,28 @@
+/*
+ * 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.extensions;
+
+public class PDFDocumentInformationExtension extends PDFDictionaryExtension {
+
+    PDFDocumentInformationExtension() {
+        super(PDFDictionaryType.Info);
+    }
+
+}

Property changes on: src/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationExtension.java
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Revision Id
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java
===================================================================
--- src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java	(revision 1599339)
+++ src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java	(working copy)
@@ -67,6 +67,8 @@
             foObjs.put(PDFObjectType.Reference.elementName(), new PDFReferenceElementMaker());
             // pdf:string
             foObjs.put(PDFObjectType.String.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.String));
+            // pdf:info
+            foObjs.put(PDFDictionaryType.Info.elementName(), new PDFDocumentInformationElementMaker());
         }
     }
 
@@ -88,6 +90,12 @@
         }
     }
 
+    static class PDFDocumentInformationElementMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new PDFDocumentInformationElement(parent);
+        }
+    }
+
     static class PDFDictionaryElementMaker extends ElementMapping.Maker {
         public FONode make(FONode parent) {
             return new PDFDictionaryElement(parent, PDFDictionaryType.Dictionary);
Index: src/java/org/apache/fop/render/pdf/extensions/PDFExtensionEventProducer.java
===================================================================
--- src/java/org/apache/fop/render/pdf/extensions/PDFExtensionEventProducer.java	(revision 0)
+++ src/java/org/apache/fop/render/pdf/extensions/PDFExtensionEventProducer.java	(working copy)
@@ -0,0 +1,37 @@
+/*
+ * 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.extensions;
+
+import org.xml.sax.Locator;
+
+import org.apache.fop.events.EventProducer;
+
+public interface PDFExtensionEventProducer extends EventProducer {
+
+    /** Reserved keyword.
+     *
+     * @param source the object source
+     * @param locator the location in the source
+     * @param keyword the offending keyword
+     * @event.severity ERROR
+     */
+    void reservedKeyword(Object source, Locator locator, String keyword);
+
+}

Property changes on: src/java/org/apache/fop/render/pdf/extensions/PDFExtensionEventProducer.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+Revision Id
\ No newline at end of property
Index: src/java/org/apache/fop/render/pdf/extensions/PDFExtensionEventProducer.xml
===================================================================
--- src/java/org/apache/fop/render/pdf/extensions/PDFExtensionEventProducer.xml	(revision 0)
+++ src/java/org/apache/fop/render/pdf/extensions/PDFExtensionEventProducer.xml	(working copy)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
+  <message key="locator">[ (See position {locator})| (See {#gatherContextInfo})| (No context info available)]</message>
+  <message key="reservedKeyword">{keyword} is a reserved keyword{{locator}}. Please use a different keyword for you custom property.</message>
+</catalogue>

Property changes on: src/java/org/apache/fop/render/pdf/extensions/PDFExtensionEventProducer.xml
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Revision Id
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
===================================================================
--- src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java	(revision 1599339)
+++ src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java	(working copy)
@@ -107,6 +107,9 @@
                     page.setProperty(PDFPageExtension.PROPERTY_PAGE_NUMBERS, pageNumbers);
                 }
                 collections.push(page);
+            } else if (PDFDictionaryType.Info.elementName().equals(localName)) {
+                PDFDocumentInformationExtension info = new PDFDocumentInformationExtension();
+                collections.push(info);
             } else if (PDFObjectType.hasValueOfElementName(localName)) {
                 PDFCollectionEntryExtension entry;
                 if (PDFObjectType.Reference.elementName().equals(localName)) {
Index: test/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationElementTestCase.java
===================================================================
--- test/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationElementTestCase.java	(revision 0)
+++ test/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationElementTestCase.java	(working copy)
@@ -0,0 +1,72 @@
+/*
+ * 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.extensions;
+
+import java.io.FileInputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import org.apache.xmlgraphics.util.QName;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.events.EventChecker;
+import org.apache.fop.fo.FODocumentParser;
+import org.apache.fop.fo.FODocumentParser.FOEventHandlerFactory;
+import org.apache.fop.fo.FOEventHandler;
+import org.apache.fop.fo.FOValidationEventProducer;
+import org.apache.fop.fo.LoadingException;
+import org.apache.fop.fotreetest.DummyFOEventHandler;
+
+public class PDFDocumentInformationElementTestCase {
+
+    private FODocumentParser parser = FODocumentParser.newInstance(new FOEventHandlerFactory() {
+
+        public FOEventHandler newFOEventHandler(FOUserAgent foUserAgent) {
+            return new DummyFOEventHandler(foUserAgent);
+        }
+    });
+
+    @Test(expected = LoadingException.class)
+    public void illegalChild() throws Exception {
+        Map<String, Object> expectedParams = new HashMap<String, Object>();
+        expectedParams.put("offendingNode", new QName(PDFElementMapping.NAMESPACE, "dictionary"));
+        runTest("invalid-child.fo", FOValidationEventProducer.class.getName() + ".invalidChild", expectedParams);
+    }
+
+    @Test
+    public void standardKeyword() throws Exception {
+        Map<String, Object> expectedParams = new HashMap<String, Object>();
+        expectedParams.put("keyword", "Creator");
+        runTest("reserved-keyword.fo", PDFExtensionEventProducer.class.getName() + ".reservedKeyword", expectedParams);
+    }
+
+    private void runTest(String testCase, String expectedEventKey, Map<String, Object> expectedEventParams)
+            throws Exception {
+        EventChecker eventChecker = new EventChecker(expectedEventKey, expectedEventParams);
+        parser.setEventListener(eventChecker);
+        try {
+            parser.parse(new FileInputStream("test/pdf/extensions/document-information/" + testCase));
+        } finally {
+            eventChecker.end();
+        }
+    }
+}

Property changes on: test/java/org/apache/fop/render/pdf/extensions/PDFDocumentInformationElementTestCase.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+Revision Id
\ No newline at end of property
Index: test/pdf/extensions/document-information/invalid-child.fo
===================================================================
--- test/pdf/extensions/document-information/invalid-child.fo	(revision 0)
+++ test/pdf/extensions/document-information/invalid-child.fo	(working copy)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" 
+  xmlns:pdf="http://xmlgraphics.apache.org/fop/extensions/pdf">
+  <fo:layout-master-set>
+    <fo:simple-page-master master-name="page"
+      page-height="320pt" page-width="420pt" margin="10pt">
+      <fo:region-body/>
+    </fo:simple-page-master>
+  </fo:layout-master-set>
+  <fo:declarations>
+    <pdf:info>
+      <pdf:dictionary key="MyDictionary">
+        <pdf:name key="key1">value1</pdf:name>
+        <pdf:name key="key2">value2</pdf:name>
+      </pdf:dictionary>
+    </pdf:info>
+  </fo:declarations>
+
+  <fo:page-sequence master-reference="page">
+    <fo:flow flow-name="xsl-region-body" text-align="justify">
+      <fo:block>pdf:info only accepts pdf:name child elements.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+
+</fo:root>

Property changes on: test/pdf/extensions/document-information/invalid-child.fo
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: test/pdf/extensions/document-information/reserved-keyword.fo
===================================================================
--- test/pdf/extensions/document-information/reserved-keyword.fo	(revision 0)
+++ test/pdf/extensions/document-information/reserved-keyword.fo	(working copy)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" 
+  xmlns:pdf="http://xmlgraphics.apache.org/fop/extensions/pdf">
+  <fo:layout-master-set>
+    <fo:simple-page-master master-name="page"
+      page-height="320pt" page-width="420pt" margin="10pt">
+      <fo:region-body/>
+    </fo:simple-page-master>
+  </fo:layout-master-set>
+  <fo:declarations>
+    <pdf:info>
+      <pdf:name key="Creator">MyCreator</pdf:name>
+    </pdf:info>
+  </fo:declarations>
+
+  <fo:page-sequence master-reference="page">
+    <fo:flow flow-name="xsl-region-body" text-align="justify">
+      <fo:block>pdf:info only accepts pdf:name child elements.</fo:block>
+    </fo:flow>
+  </fo:page-sequence>
+
+</fo:root>

Property changes on: test/pdf/extensions/document-information/reserved-keyword.fo
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
