</fo:layout-master-set>
]]></source>
<p>The no-operation extension element can appear as child of
- simple-page-master (appears after "Begin Page" BPG),
- page-sequence (appears after "Begin Named Page Group" BNG
- and declarations (appears after "Begin Document" BDT).
+ <code>simple-page-master</code> (appears after "Begin Page" BPG),
+ <code>page-sequence</code> (appears after "Begin Named Page Group" BNG
+ and <code>declarations</code> (appears after "Begin Document" BDT).
Multiple no-operation extension elements inside the same formatting object are allowed.
Each NOP will appear right after the respective "Begin" field indicated above even if it
- is specified as the last child under its parent. However, the order inside the parent
+ is specified as the last child under its parent. The order inside the parent
will be maintained.
- The name attribute is mandatory but will not appear inside the AFP stream.
+ The "placement" attribute can be used to have the NOP appear before
+ the "End" field of the object rather than after the "Begin" field. Specify
+ <code>placement="before-end"</code> to do that. Please note that, at the moment, this only
+ has an effect for NOPs that are children of the <code>page-sequence</code> formatting
+ object.
+ The "name" attribute is mandatory but will not appear inside the AFP stream.
</p>
</section>
<section id="afp-invoke-medium-map">
import java.awt.Dimension;
import java.awt.geom.AffineTransform;
import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import org.apache.fop.afp.AFPDitheredRectanglePainter;
import org.apache.fop.render.afp.extensions.AFPPageOverlay;
import org.apache.fop.render.afp.extensions.AFPPageSegmentElement;
import org.apache.fop.render.afp.extensions.AFPPageSetup;
+import org.apache.fop.render.afp.extensions.ExtensionPlacement;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
import org.apache.fop.render.intermediate.IFDocumentHandler;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
private Location location = Location.ELSEWHERE;
+ /** temporary holds extensions that have to be deferred until the end of the page-sequence */
+ private List<AFPPageSetup> deferredPageSequenceExtensions
+ = new java.util.LinkedList<AFPPageSetup>();
+
/** the shading mode for filled rectangles */
private AFPShadingMode shadingMode = AFPShadingMode.COLOR;
/** {@inheritDoc} */
public void endPageSequence() throws IFException {
- //nop
+ try {
+ //Process deferred page-sequence-level extensions
+ Iterator<AFPPageSetup> iter = this.deferredPageSequenceExtensions.iterator();
+ while (iter.hasNext()) {
+ AFPPageSetup aps = iter.next();
+ iter.remove();
+ if (AFPElementMapping.NO_OPERATION.equals(aps.getElementName())) {
+ handleNOP(aps);
+ } else {
+ throw new UnsupportedOperationException("Don't know how to handle " + aps);
+ }
+ }
+
+ //End page sequence
+ dataStream.endPageGroup();
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endPageSequence()", ioe);
+ }
+ this.location = Location.ELSEWHERE;
}
/**
}
} else if (AFPElementMapping.NO_OPERATION.equals(element)) {
switch (this.location) {
- case IN_DOCUMENT_HEADER:
case FOLLOWING_PAGE_SEQUENCE:
- case IN_PAGE_HEADER:
- String content = aps.getContent();
- if (content != null) {
- dataStream.createNoOperation(content);
+ if (aps.getPlacement() == ExtensionPlacement.BEFORE_END) {
+ this.deferredPageSequenceExtensions.add(aps);
+ break;
}
+ case IN_DOCUMENT_HEADER:
+ case IN_PAGE_HEADER:
+ handleNOP(aps);
break;
default:
throw new IFException(
}
}
+ private void handleNOP(AFPPageSetup nop) {
+ String content = nop.getContent();
+ if (content != null) {
+ dataStream.createNoOperation(content);
+ }
+ }
+
// ---=== AFPCustomizable ===---
/** {@inheritDoc} */
private ObjectBuiltListener listener;
/** {@inheritDoc} */
+ @Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
boolean handled = false;
}
/** {@inheritDoc} */
+ @Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (AFPExtensionAttachment.CATEGORY.equals(uri)) {
if (AFPElementMapping.INCLUDE_FORM_MAP.equals(localName)) {
pageSetupExtn = new AFPPageSetup(localName);
this.returnedObject = pageSetupExtn;
}
- String name = lastAttributes.getValue("name");
+ String name = lastAttributes.getValue(AFPPageSetup.ATT_NAME);
if (name != null) {
returnedObject.setName(name);
}
- String value = lastAttributes.getValue("value");
+ String value = lastAttributes.getValue(AFPPageSetup.ATT_VALUE);
if (value != null && pageSetupExtn != null) {
pageSetupExtn.setValue(value);
}
+ String placement = lastAttributes.getValue(AFPPageSetup.ATT_PLACEMENT);
+ if (placement != null && placement.length() > 0) {
+ pageSetupExtn.setPlacement(ExtensionPlacement.fromXMLValue(placement));
+ }
if (content.length() > 0 && pageSetupExtn != null) {
pageSetupExtn.setContent(content.toString());
content.setLength(0); //Reset text buffer (see characters())
}
/** {@inheritDoc} */
+ @Override
public void characters(char[] ch, int start, int length) throws SAXException {
content.append(ch, start, length);
}
/**
* {@inheritDoc}
*/
+ @Override
public void endDocument() throws SAXException {
if (listener != null) {
listener.notifyObjectBuilt(getObject());
/** value attribute */
protected static final String ATT_VALUE = "value";
+ /** placement attribute */
+ protected static final String ATT_PLACEMENT = "placement";
+
/**
* the extension content
*/
*/
protected String value;
+ /** defines where to place the extension in the generated file */
+ protected ExtensionPlacement placement = ExtensionPlacement.DEFAULT;
+
/**
* Default constructor.
*
this.content = content;
}
+ /**
+ * Returns the intended placement of the extension inside the generated file.
+ * @return the intended placement
+ */
+ public ExtensionPlacement getPlacement() {
+ return this.placement;
+ }
+
+ /**
+ * Sets the intended placement of the extension inside the generated file.
+ * @param placement the intended placement
+ */
+ public void setPlacement(ExtensionPlacement placement) {
+ if (!AFPElementMapping.NO_OPERATION.equals(getElementName())) {
+ throw new UnsupportedOperationException(
+ "The attribute 'placement' can currently only be set for NOPs!");
+ }
+ this.placement = placement;
+ }
+
/** {@inheritDoc} */
public void toSAX(ContentHandler handler) throws SAXException {
AttributesImpl atts = new AttributesImpl();
if (value != null && value.length() > 0) {
atts.addAttribute(null, ATT_VALUE, ATT_VALUE, "CDATA", value);
}
+ if (this.placement != ExtensionPlacement.DEFAULT) {
+ atts.addAttribute(null, ATT_PLACEMENT, ATT_PLACEMENT, "CDATA", placement.getXMLValue());
+ }
handler.startElement(CATEGORY, elementName, elementName, atts);
if (content != null && content.length() > 0) {
char[] chars = content.toCharArray();
}
/** {@inheritDoc} */
+ @Override
public String toString() {
- return "AFPPageSetup(element-name=" + getElementName()
- + " name=" + getName() + " value=" + getValue() + ")";
+ StringBuilder sb = new StringBuilder("AFPPageSetup(");
+ sb.append("element-name=").append(getElementName());
+ sb.append(" name=").append(getName());
+ sb.append(" value=").append(getValue());
+ if (getPlacement() != ExtensionPlacement.DEFAULT) {
+ sb.append(" placement=").append(getPlacement());
+ }
+ sb.append(")");
+ return sb.toString();
}
}
/**
* This class extends the org.apache.fop.extensions.ExtensionObj class. The
- * object faciliates extraction of elements from formatted objects based on
+ * object facilitates extraction of elements from formatted objects based on
* the static list as defined in the AFPElementMapping implementation.
- * <p/>
*/
public class AFPPageSetupElement extends AbstractAFPExtensionObject {
- private static final String ATT_VALUE = "value";
private static final String ATT_SRC = "src";
/**
missingPropertyError(ATT_SRC);
}
} else if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(elementName)) {
- String attr = attlist.getValue(ATT_VALUE);
+ String attr = attlist.getValue(AFPPageSetup.ATT_VALUE);
if (attr != null && attr.length() > 0) {
pageSetup.setValue(attr);
} else {
- missingPropertyError(ATT_VALUE);
+ missingPropertyError(AFPPageSetup.ATT_VALUE);
}
}
+ String placement = attlist.getValue(AFPPageSetup.ATT_PLACEMENT);
+ if (placement != null && placement.length() > 0) {
+ pageSetup.setPlacement(ExtensionPlacement.fromXMLValue(placement));
+ }
}
/** {@inheritDoc} */
--- /dev/null
+/*
+ * 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.afp.extensions;
+
+/**
+ * An enumeration for placement instruction for AFP extensions.
+ */
+public enum ExtensionPlacement {
+
+ /** Place the extension at its default/usual position. */
+ DEFAULT,
+ /** Place the extension right before the "End" structured field. */
+ BEFORE_END;
+
+ /**
+ * Returns the XML value that corresponds to this enum value.
+ * @return the XML value
+ */
+ public String getXMLValue() {
+ String xmlName = name().toLowerCase();
+ xmlName = xmlName.replace('_', '-');
+ return xmlName;
+ }
+
+ /**
+ * Returns the enum value from the given XML value for this enumeration.
+ * @param value the XML value
+ * @return the enum value
+ */
+ public static ExtensionPlacement fromXMLValue(String value) {
+ String name = value.toUpperCase();
+ name = name.replace('-', '_');
+ return ExtensionPlacement.valueOf(name);
+ }
+
+}
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
+ <action context="Renderers" dev="JM" type="add">
+ Added option to place AFP NOPs right before the end of a named page group (page-sequence),
+ rather than after the start.
+ </action>
<action context="Renderers" dev="JM" type="add">
Added option for PostScript output to optimize for file size rather than quality.
</action>