]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
AFP Renderer as per IP clearance (http://mail-archives.apache.org/mod_mbox/xmlgraphic...
authorManuel Mall <manuel@apache.org>
Thu, 27 Apr 2006 15:08:17 +0000 (15:08 +0000)
committerManuel Mall <manuel@apache.org>
Thu, 27 Apr 2006 15:08:17 +0000 (15:08 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@397562 13f79535-47bb-0310-9956-ffa450edef68

63 files changed:
src/sandbox/META-INF/services/org.apache.fop.render.Renderer
src/sandbox/org/apache/fop/render/afp/AFPFontAttributes.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/AFPFontColor.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/AFPRenderer.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/AFPRendererMaker.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/exceptions/FontRuntimeException.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/exceptions/NestedRuntimeException.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/exceptions/RendererRuntimeException.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/extensions/AFPAttribute.java [new file with mode: 0755]
src/sandbox/org/apache/fop/render/afp/extensions/AFPElementMapping.java [new file with mode: 0755]
src/sandbox/org/apache/fop/render/afp/extensions/AFPPageSetup.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/extensions/AbstractAFPExtensionObject.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/fonts/AFPFont.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/fonts/AFPFontInfo.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/fonts/AFPFontReader.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/fonts/CharacterSet.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/fonts/CharacterSetOrientation.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/fonts/FopCharacterSet.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/fonts/OutlineFont.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/fonts/RasterFont.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/AFPConstants.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/AFPDataStream.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/AbstractAFPObject.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/AbstractPageObject.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ActiveEnvironmentGroup.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/Document.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/EndPageGroup.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/IMImageObject.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ImageCellPosition.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ImageContent.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ImageDataDescriptor.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ImageInputDescriptor.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ImageObject.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ImageOutputControl.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ImageRasterData.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ImageRasterPattern.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ImageSegment.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ImageSizeParameter.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/IncludeObject.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/IncludePageOverlay.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/IncludePageSegment.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/InvokeMediumMap.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/MapCodedFont.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/MapPageOverlay.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/MaximumSizeExceededException.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ObjectAreaDescriptor.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ObjectAreaPosition.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ObjectEnvironmentGroup.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/Overlay.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/PageDescriptor.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/PageGroup.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/PageObject.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/PresentationTextData.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/PresentationTextDescriptor.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/PresentationTextObject.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/ResourceGroup.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/TagLogicalElement.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/modca/TagLogicalElementBean.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/tools/BinaryUtils.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/tools/DTDEntityResolver.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/tools/StringUtils.java [new file with mode: 0644]
src/sandbox/org/apache/fop/render/afp/tools/StructuredFieldReader.java [new file with mode: 0644]

index d75e81eb2b5cb25a6b425acdeb2b6a1645de1021..dcd2b38c8b9e89eecefab6053e94fc11231a0fe3 100644 (file)
@@ -1,2 +1,3 @@
+org.apache.fop.render.afp.AFPRendererMaker\r
 org.apache.fop.render.pcl.PCLRendererMaker\r
 org.apache.fop.render.svg.SVGRendererMaker
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/AFPFontAttributes.java b/src/sandbox/org/apache/fop/render/afp/AFPFontAttributes.java
new file mode 100644 (file)
index 0000000..2bb446c
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+import org.apache.fop.render.afp.fonts.AFPFont;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * This class encapsulates the font atributes that need to be included
+ * in the AFP data stream. This class does not assist in converting the
+ * font attributes to AFP code pages and character set values.
+ *
+ */
+public class AFPFontAttributes {
+
+    /**
+     * The font reference byte
+     */
+    private byte _fontReference;
+
+    /**
+     * The font key
+     */
+    private String _fontKey;
+
+    /**
+     * The font
+     */
+    private AFPFont _font;
+
+    /**
+     * The point size
+     */
+    private int _pointSize;
+
+    /**
+     * Constructor for the AFPFontAttributes
+     * @param fontKey the font key
+     * @param font the font
+     * @param pointSize the point size
+     */
+    public AFPFontAttributes(
+
+        String fontKey,
+        AFPFont font,
+        int pointSize) {
+
+        _fontKey = fontKey;
+        _font = font;
+        _pointSize = pointSize;
+
+    }
+    /**
+     * @return the font
+     */
+    public AFPFont getFont() {
+        return _font;
+    }
+
+    /**
+     * @return the FontKey attribute
+     */
+    public String getFontKey() {
+
+        return _fontKey + _pointSize;
+
+    }
+
+    /**
+     * @return the point size attribute
+     */
+    public int getPointSize() {
+        return _pointSize;
+    }
+
+    /**
+     * @return the FontReference attribute
+     */
+    public byte getFontReference() {
+        return _fontReference;
+    }
+
+    /**
+     * Sets the FontReference attribute
+     * @param fontReference the FontReference to set
+     */
+    public void setFontReference(int fontReference) {
+
+        String id = String.valueOf(fontReference);
+        _fontReference = BinaryUtils.convert(id)[0];
+
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/AFPFontColor.java b/src/sandbox/org/apache/fop/render/afp/AFPFontColor.java
new file mode 100644 (file)
index 0000000..be3f678
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+import java.awt.Color;
+import org.apache.fop.datatypes.ColorType;
+
+/**
+ * AFP only supports very basic colours and this object provides a simple
+ * bean for the colour attributes.
+ *
+ */
+public class AFPFontColor {
+
+    /**
+     * Red value.
+     */
+    private int _red = 0;
+
+    /**
+     * Green value.
+     */
+    private int _green = 0;
+
+    /**
+     * Blue value.
+     */
+    private int _blue = 0;
+
+    /**
+     * Constructor for the AFPColor Object
+     * @param red The red color intensity (0-255)
+     * @param green The green color intensity (0-255)
+     * @param blue The blue color intensity (0-255)
+     */
+    public AFPFontColor(int red, int green, int blue) {
+
+        _red = red;
+        _green = green;
+        _blue = blue;
+
+    }
+
+    /**
+     * Constructor for the AFPColor Object
+     * @param col the org.apache.fop.datatypes.ColorType object
+     */
+    public AFPFontColor(ColorType col) {
+
+        _red = (int)(col.getRed() * 255);
+        _green = (int)(col.getGreen() * 255);
+        _blue = (int)(col.getBlue() * 255);
+
+    }
+
+    /**
+     * Constructor for the AFPColor Object
+     * @param col The java.awt.Color object
+     */
+    public AFPFontColor(Color col) {
+
+        _red = col.getRed();
+        _green = col.getGreen();
+        _blue = col.getBlue();
+
+    }
+
+    /**
+     * Returns the blue attribute
+     * @return int
+     */
+    public int getBlue() {
+        return _blue;
+    }
+
+    /**
+     * Returns the green attribute
+     * @return int
+     */
+    public int getGreen() {
+        return _green;
+    }
+
+    /**
+     * Returns the red attribute
+     * @return int
+     */
+    public int getRed() {
+        return _red;
+    }
+
+    /**
+     * Sets the blue attribute
+     * @param blue The blue value to set
+     */
+    public void setBlue(int blue) {
+        _blue = blue;
+    }
+
+    /**
+     * Sets the green attribute
+     * @param green The green value to set
+     */
+    public void setGreen(int green) {
+        _green = green;
+    }
+
+    /**
+     * Sets the red attribute
+     * @param red The red value to set
+     */
+    public void setRed(int red) {
+        _red = red;
+    }
+
+    /**
+     * Sets this color object to the same values
+     * as the given object.
+     * @param col the source color
+     */
+    public void setTo(AFPFontColor col) {
+        _red = col.getRed();
+        _green = col.getGreen();
+        _blue = col.getBlue();
+    }
+
+    /**
+     * Checks whether this object is equal to the parameter passed
+     * as an argument. If the parameter is an instance of AFPColor
+     * then the value are compared, otherwise the generalized equals
+     * method is invoked on the parent class.
+     *
+     * @param obj the object to compare
+     * @return boolean true if the object is equal
+     */
+    public boolean equals(Object obj) {
+
+        if (obj instanceof AFPFontColor) {
+            AFPFontColor c = (AFPFontColor) obj;
+            if (c.getRed() == _red
+                && c.getGreen() == _green
+                && c.getBlue() == _blue) {
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            return super.equals(obj);
+        }
+
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/AFPRenderer.java b/src/sandbox/org/apache/fop/render/afp/AFPRenderer.java
new file mode 100644 (file)
index 0000000..e4505d0
--- /dev/null
@@ -0,0 +1,1796 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.area.Block;
+import org.apache.fop.area.BlockViewport;
+import org.apache.fop.area.BodyRegion;
+import org.apache.fop.area.CTM;
+import org.apache.fop.area.OffDocumentItem;
+import org.apache.fop.area.PageViewport;
+import org.apache.fop.area.RegionReference;
+import org.apache.fop.area.RegionViewport;
+import org.apache.fop.area.Trait;
+import org.apache.fop.area.inline.Character;
+import org.apache.fop.area.inline.Leader;
+import org.apache.fop.area.inline.Image;
+import org.apache.fop.area.inline.SpaceArea;
+import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.area.inline.WordArea;
+import org.apache.fop.datatypes.ColorType;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontMetrics;
+import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.fonts.FontUtil;
+import org.apache.fop.fonts.Typeface;
+import org.apache.fop.fonts.base14.Courier;
+import org.apache.fop.fonts.base14.Helvetica;
+import org.apache.fop.fonts.base14.TimesRoman;
+import org.apache.fop.image.FopImage;
+import org.apache.fop.image.ImageFactory;
+import org.apache.fop.image.TIFFImage;
+import org.apache.fop.image.XMLImage;
+import org.apache.fop.render.AbstractPathOrientedRenderer;
+import org.apache.fop.render.Graphics2DAdapter;
+import org.apache.fop.render.afp.extensions.AFPElementMapping;
+import org.apache.fop.render.afp.extensions.AFPPageSetup;
+import org.apache.fop.render.afp.fonts.AFPFontInfo;
+import org.apache.fop.render.afp.fonts.AFPFont;
+import org.apache.fop.render.afp.fonts.CharacterSet;
+import org.apache.fop.render.afp.fonts.FopCharacterSet;
+import org.apache.fop.render.afp.fonts.OutlineFont;
+import org.apache.fop.render.afp.fonts.RasterFont;
+import org.apache.fop.render.afp.modca.AFPConstants;
+import org.apache.fop.render.afp.modca.AFPDataStream;
+import org.apache.fop.render.afp.modca.ImageObject;
+import org.apache.fop.render.afp.modca.PageObject;
+
+
+/**
+ * This is an implementation of a FOP Renderer that renders areas to AFP.
+ * <p>
+ * A renderer is primarily designed to convert a given area tree into the output
+ * document format. It should be able to produce pages and fill the pages with
+ * the text and graphical content. Usually the output is sent to an output
+ * stream. Some output formats may support extra information that is not
+ * available from the area tree or depends on the destination of the document.
+ * Each renderer is given an area tree to render to its output format. The area
+ * tree is simply a representation of the pages and the placement of text and
+ * graphical objects on those pages.
+ * </p>
+ * <p>
+ * The renderer will be given each page as it is ready and an output stream to
+ * write the data out. All pages are supplied in the order they appear in the
+ * document. In order to save memory it is possble to render the pages out of
+ * order. Any page that is not ready to be rendered is setup by the renderer
+ * first so that it can reserve a space or reference for when the page is ready
+ * to be rendered.The renderer is responsible for managing the output format and
+ * associated data and flow.
+ * </p>
+ * <p>
+ * Each renderer is totally responsible for its output format. Because font
+ * metrics (and therefore layout) are obtained in two different ways depending
+ * on the renderer, the renderer actually sets up the fonts being used. The font
+ * metrics are used during the layout process to determine the size of
+ * characters.
+ * </p>
+ * <p>
+ * The render context is used by handlers. It contains information about the
+ * current state of the renderer, such as the page, the position, and any other
+ * miscellanous objects that are required to draw into the page.
+ * </p>
+ * <p>
+ * A renderer is created by implementing the Renderer interface. However, the
+ * AbstractRenderer does most of what is needed, including iterating through the
+ * tree parts, so it is this that is extended. This means that this object only
+ * need to implement the basic functionality such as text, images, and lines.
+ * AbstractRenderer's methods can easily be overridden to handle things in a
+ * different way or do some extra processing.
+ * </p>
+ * <p>
+ * The relevent AreaTree structures that will need to be rendered are Page,
+ * Viewport, Region, Span, Block, Line, Inline. A renderer implementation
+ * renders each individual page, clips and aligns child areas to a viewport,
+ * handle all types of inline area, text, image etc and draws various lines and
+ * rectangles.
+ * </p>
+ *
+ * Note: There are specific extensions that have been added to the
+ * FO. They are specific to their location within the FO and have to be
+ * processed accordingly (ie. at the start or end of the page).
+ *
+ */
+public class AFPRenderer extends AbstractPathOrientedRenderer {
+
+    /**
+     * AFP factor for a 240 resolution = 72000/240 = 300
+     */
+    private static final int DPI_CONVERSION_FACTOR_240 = 300;
+
+    /**
+     * The afp data stream object responsible for generating afp data
+     */
+    private AFPDataStream _afpDataStream = null;
+
+    /**
+     * The map of afp root extensions
+     */
+    private HashMap _rootExtensionMap = null;
+
+    /**
+     * The map of page segments
+     */
+    private HashMap _pageSegmentsMap = null;
+
+    /**
+     * The fonts on the current page
+     */
+    private HashMap _currentPageFonts = null;
+
+    /**
+     * The current color object
+     */
+    private AFPFontColor _currentColor = null;
+
+    /**
+     * The page font number counter, used to determine the next font reference
+     */
+    private int _pageFontCounter = 0;
+
+    /**
+     * The current font family
+     */
+    private String _currentFontFamily = "";
+
+    /**
+     * The current font size
+     */
+    private int _currentFontSize = 0;
+
+    /**
+     * The Options to be set on the AFPRenderer
+     */
+    private Map _afpOptions = null;
+
+    /**
+     * The page width
+     */
+    private int _pageWidth = 0;
+
+    /**
+     * The page height
+     */
+    private int _pageHeight = 0;
+
+    /**
+     * The current page sequence id
+     */
+    private String _pageSequenceId = null;
+
+    /**
+     * The portrait rotation
+     */
+    private int _portraitRotation = 0;
+
+    /**
+     * The landscape rotation
+     */
+    private int _landscapeRotation = 270;
+
+    /**
+     * The line cache, avoids drawing duplicate lines in tables.
+     */
+    private HashSet _lineCache = null;
+
+    /**
+     * The current x position for line drawing
+     */
+    private float _x;
+
+    /**
+     * The current y position for line drawing
+     */
+    private float _y;
+
+    /**
+     * The map of saved incomplete pages
+     */
+    private Map _pages = null;
+
+    /**
+     * Flag to the set the output object type for images
+     */
+    private boolean colorImages = false;
+
+    /**
+     * Default value for image depth
+     */
+    private int bitsPerPixel = 8;
+
+    /**
+     * Constructor for AFPRenderer.
+     */
+    public AFPRenderer() {
+        super();
+    }
+
+    /**
+     * Set up the font info
+     *
+     * @param inFontInfo  font info to set up
+     */
+    public void setupFontInfo(FontInfo inFontInfo) {
+        this.fontInfo = inFontInfo;
+        int num = 1;
+        if (this.fontList != null && this.fontList.size() > 0) {
+            for (Iterator it = this.fontList.iterator(); it.hasNext(); ) {
+                AFPFontInfo afi = (AFPFontInfo)it.next();
+                AFPFont bf = (AFPFont)afi.getAFPFont();
+                for (Iterator it2 = afi.getFontTriplets().iterator(); it2.hasNext(); ) {
+                    FontTriplet ft = (FontTriplet)it2.next();
+                    this.fontInfo.addFontProperties("F" + num, ft.getName()
+                                                    , ft.getStyle(), ft.getWeight());
+                    this.fontInfo.addMetrics("F" + num, bf);
+                    num++;
+                }
+            }
+        } else {
+            log.warn("No AFP fonts configured - using default setup");
+        }
+        if (this.fontInfo.fontLookup("sans-serif", "normal", 400) == null) {
+            CharacterSet cs  = new FopCharacterSet("T1V10500", "Cp500", "CZH200  ", 1, new Helvetica());
+            AFPFont bf = new OutlineFont("Helvetica", cs);
+            this.fontInfo.addFontProperties("F" + num, "sans-serif", "normal", 400);
+            this.fontInfo.addMetrics("F" + num, bf);
+            num++;
+        }
+        if (this.fontInfo.fontLookup("serif", "normal", 400) == null) {
+            CharacterSet cs  = new FopCharacterSet("T1V10500", "Cp500", "CZN200  ", 1, new TimesRoman());
+            AFPFont bf = new OutlineFont("Helvetica", cs);
+            this.fontInfo.addFontProperties("F" + num, "serif", "normal", 400);
+            this.fontInfo.addMetrics("F" + num, bf);
+            num++;
+        }
+        if (this.fontInfo.fontLookup("monospace", "normal", 400) == null) {
+            CharacterSet cs  = new FopCharacterSet("T1V10500", "Cp500", "CZ4200  ", 1, new Courier());
+            AFPFont bf = new OutlineFont("Helvetica", cs);
+            this.fontInfo.addFontProperties("F" + num, "monospace", "normal", 400);
+            this.fontInfo.addMetrics("F" + num, bf);
+            num++;
+        }
+        if (this.fontInfo.fontLookup("any", "normal", 400) == null) {
+            FontTriplet ft = this.fontInfo.fontLookup("sans-serif", "normal", 400);
+            this.fontInfo.addFontProperties(this.fontInfo.getInternalFontKey(ft), "any", "normal", 400);
+        }
+    }
+
+    /**
+     */
+    private AFPFontInfo buildFont(Configuration fontCfg, String _path)
+        throws ConfigurationException {
+
+        Configuration[] triple = fontCfg.getChildren("font-triplet");
+        List tripleList = new java.util.ArrayList();
+        if (triple.length == 0) {
+            log.error("Mandatory font configuration element '<font-triplet...' is missing");
+            return null;
+        }
+        for (int j = 0; j < triple.length; j++) {
+            int weight = FontUtil.parseCSS2FontWeight(triple[j].getAttribute("weight"));
+            tripleList.add(new FontTriplet(triple[j].getAttribute("name"),
+                                           triple[j].getAttribute("style"),
+                                           weight));
+        }
+
+        //build the fonts
+        Configuration afpFontCfg = fontCfg.getChild("afp-font");
+        if (afpFontCfg == null) {
+            log.error("Mandatory font configuration element '<afp-font...' is missing");
+            return null;
+        }
+        String path = afpFontCfg.getAttribute("path", _path);
+        String type = afpFontCfg.getAttribute("type");
+        if (type == null) {
+            log.error("Mandatory afp-font configuration attribute 'type=' is missing");
+            return null;
+        }
+        String codepage = afpFontCfg.getAttribute("codepage");
+        if (codepage == null) {
+            log.error("Mandatory afp-font configuration attribute 'code=' is missing");
+            return null;
+        }
+        String encoding = afpFontCfg.getAttribute("encoding");
+        if (encoding == null) {
+            log.error("Mandatory afp-font configuration attribute 'encoding=' is missing");
+            return null;
+        }
+
+        if ("raster".equalsIgnoreCase(type)) {
+
+            String name = afpFontCfg.getAttribute("name", "Unknown");
+
+            // Create a new font object
+            RasterFont font = new RasterFont(name);
+
+            Configuration[] rasters = afpFontCfg.getChildren("afp-raster-font");
+            if (rasters.length == 0) {
+                log.error("Mandatory font configuration elements '<afp-raster-font...' are missing");
+                return null;
+            }
+            for (int j = 0; j < rasters.length; j++) {
+                Configuration rasterCfg = rasters[j];
+
+                String characterset = rasterCfg.getAttribute("characterset");
+                if (characterset == null) {
+                    log.error("Mandatory afp-raster-font configuration attribute 'characterset=' is missing");
+                    return null;
+                }
+                int size = rasterCfg.getAttributeAsInteger("size");
+                String base14 = rasterCfg.getAttribute("base14-font", null);
+
+                if (base14 != null) {
+                    try {
+                        Class clazz = Class.forName("org.apache.fop.fonts.base14."
+                            + base14);
+                        try {
+                            Typeface tf = (Typeface)clazz.newInstance();
+                            font.addCharacterSet(size, new FopCharacterSet(
+                                codepage, encoding, characterset, size, tf));
+                        } catch (Exception ie) {
+                            String msg = "The base 14 font class " + clazz.getName()
+                                + " could not be instantiated";
+                            log.error(msg);
+                        }
+                    } catch (ClassNotFoundException cnfe) {
+                        String msg = "The base 14 font class for " + characterset
+                            + " could not be found";
+                        log.error(msg);
+                    }
+                } else {
+                    font.addCharacterSet(size, new CharacterSet(
+                        codepage, encoding, characterset, path));
+                }
+            }
+            return new AFPFontInfo(font, tripleList);
+
+        } else if ("outline".equalsIgnoreCase(type)) {
+
+            String characterset = afpFontCfg.getAttribute("characterset");
+            if (characterset == null) {
+                log.error("Mandatory afp-font configuration attribute 'characterset=' is missing");
+                return null;
+            }
+            String name = afpFontCfg.getAttribute("name", characterset);
+
+            CharacterSet characterSet = null;
+
+            String base14 = afpFontCfg.getAttribute("base14-font", null);
+
+            if (base14 != null) {
+                try {
+                    Class clazz = Class.forName("org.apache.fop.fonts.base14."
+                        + base14);
+                    try {
+                        Typeface tf = (Typeface)clazz.newInstance();
+                        characterSet = new FopCharacterSet(
+                                codepage, encoding, characterset, 1, tf);
+                    } catch (Exception ie) {
+                        String msg = "The base 14 font class " + clazz.getName()
+                            + " could not be instantiated";
+                        log.error(msg);
+                    }
+                } catch (ClassNotFoundException cnfe) {
+                    String msg = "The base 14 font class for " + characterset
+                        + " could not be found";
+                    log.error(msg);
+                }
+            } else {
+                characterSet = new CharacterSet(codepage, encoding, characterset, path);
+            }
+            // Create a new font object
+            OutlineFont font = new OutlineFont(name, characterSet);
+            return new AFPFontInfo(font, tripleList);
+        } else {
+            log.error("No or incorrect type attribute");
+        }
+        return null;
+    }
+
+    /**
+     * Builds a list of AFPFontInfo objects for use with the setup() method.
+     * @param cfg Configuration object
+     * @return List the newly created list of fonts
+     * @throws ConfigurationException if something's wrong with the config data
+     */
+    public List buildFontListFromConfiguration(Configuration cfg)
+            throws ConfigurationException {
+        List fontList = new java.util.ArrayList();
+        Configuration[] font = cfg.getChild("fonts").getChildren("font");
+        for (int i = 0; i < font.length; i++) {
+            AFPFontInfo afi = buildFont(font[i], null);
+            if (afi != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("Adding font " + afi.getAFPFont().getFontName());
+                    for (int j = 0; j < afi.getFontTriplets().size(); ++j) {
+                        FontTriplet triplet = (FontTriplet) afi.getFontTriplets().get(j);
+                        log.debug("Font triplet "
+                                  + triplet.getName() + ", "
+                                  + triplet.getStyle() + ", "
+                                  + triplet.getWeight());
+                    }
+                }
+
+                fontList.add(afi);
+            }
+        }
+        return fontList;
+    }
+
+    /**
+     * Configure the AFP renderer.
+     * Get the configuration to be used for fonts etc.
+     * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
+     */
+    public void configure(Configuration cfg) throws ConfigurationException {
+        //Font configuration
+        this.fontList = buildFontListFromConfiguration(cfg);
+        Configuration images = cfg.getChild("images");
+        if (!"color".equalsIgnoreCase(images.getAttribute("mode", "b+w"))) {
+            bitsPerPixel = images.getAttributeAsInteger("bits-per-pixel", 8);
+            switch (bitsPerPixel) {
+                case 1:
+                case 4:
+                case 8:
+                    break;
+                default:
+                    log.warn("Invalid bits_per_pixel value, must be 1, 4 or 8.");
+                    bitsPerPixel = 8;
+                    break;
+            }
+        } else {
+            colorImages = true;
+        }
+
+    }
+
+    /**
+     * @see org.apache.fop.render.Renderer#setUserAgent(FOUserAgent)
+     */
+    public void setUserAgent(FOUserAgent agent) {
+        super.setUserAgent(agent);
+    }
+
+    /**
+     * @see org.apache.fop.render.Renderer#startRenderer(java.io.OutputStream)
+     */
+    public void startRenderer(OutputStream outputStream) throws IOException {
+        _currentPageFonts = new HashMap();
+        _currentColor = new AFPFontColor(255, 255, 255);
+        _afpDataStream = new AFPDataStream();
+        _afpDataStream.setPortraitRotation(_portraitRotation);
+        _afpDataStream.setLandscapeRotation(_landscapeRotation);
+        _afpDataStream.startDocument(outputStream);
+    }
+
+    /**
+     * @see org.apache.fop.render.Renderer#stopRenderer(java.io.OutputStream)
+     */
+    public void stopRenderer() throws IOException {
+        _afpDataStream.endDocument();
+    }
+
+    /**
+     * @see org.apache.fop.render.Renderer#supportsOutOfOrder()
+     */
+    public boolean supportsOutOfOrder() {
+        //return false;
+        return true;
+    }
+
+    /**
+     * Prepare a page for rendering. This is called if the renderer supports
+     * out of order rendering. The renderer should prepare the page so that a
+     * page further on in the set of pages can be rendered. The body of the
+     * page should not be rendered. The page will be rendered at a later time
+     * by the call to render page.
+     *
+     * @see org.apache.fop.render.Renderer#preparePage(PageViewport)
+     */
+    public void preparePage(PageViewport page) {
+        // initializeRootExtensions(page);
+
+        _currentFontFamily = "";
+        _currentFontSize = 0;
+        _pageFontCounter = 0;
+        _currentPageFonts.clear();
+        _lineCache = new HashSet();
+
+        Rectangle2D bounds = page.getViewArea();
+
+        _pageWidth = mpts2units(bounds.getWidth());
+        _pageHeight = mpts2units(bounds.getHeight());
+
+        // renderPageGroupExtensions(page);
+
+        _afpDataStream.startPage(_pageWidth, _pageHeight, 0);
+
+        renderPageObjectExtensions(page);
+
+        if (_pages == null) {
+            _pages = new HashMap();
+        }
+        _pages.put(page, _afpDataStream.savePage());
+
+    }
+
+    /**
+     * @see org.apache.fop.render.Renderer#processOffDocumentItem(OffDocumentItem)
+     */
+    public void processOffDocumentItem(OffDocumentItem odi) {
+        // TODO
+    }
+
+    /** @see org.apache.fop.render.Renderer#getGraphics2DAdapter() */
+    public Graphics2DAdapter getGraphics2DAdapter() {
+        // TODO
+        return null;
+    }
+
+    /**
+     * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM, Rectangle2D)
+     */
+    public void startVParea(CTM ctm, Rectangle2D clippingRect) {
+        // dummy not used
+    }
+
+    /**
+     * @see org.apache.fop.render.AbstractRenderer#endVParea()
+     */
+    public void endVParea() {
+        // dummy not used
+    }
+
+    /**
+     * Renders a region viewport. <p>
+     *
+     * The region may clip the area and it establishes a position from where
+     * the region is placed.</p>
+     *
+     * @param port  The region viewport to be rendered
+     */
+    public void renderRegionViewport(RegionViewport port) {
+        if (port != null) {
+            Rectangle2D view = port.getViewArea();
+            // The CTM will transform coordinates relative to
+            // this region-reference area into page coords, so
+            // set origin for the region to 0,0.
+            currentBPPosition = 0;
+            currentIPPosition = 0;
+
+            RegionReference regionReference = port.getRegionReference();
+            handleRegionTraits(port);
+
+            /*
+            _afpDataStream.startOverlay(mpts2units(view.getX())
+                , mpts2units(view.getY())
+                , mpts2units(view.getWidth())
+                , mpts2units(view.getHeight())
+                , rotation);
+             */
+
+            pushViewPortPos(new ViewPortPos(view, regionReference.getCTM()));
+
+            if (regionReference.getRegionClass() == FO_REGION_BODY) {
+                renderBodyRegion((BodyRegion) regionReference);
+            } else {
+                renderRegion(regionReference);
+            }
+            /*
+            _afpDataStream.endOverlay();
+             */
+            popViewPortPos();
+        }
+    }
+
+    /**
+     * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List)
+     */
+    protected void renderBlockViewport(BlockViewport bv, List children) {
+        // clip and position viewport if necessary
+
+        // save positions
+        int saveIP = currentIPPosition;
+        int saveBP = currentBPPosition;
+        //String saveFontName = currentFontName;
+
+        CTM ctm = bv.getCTM();
+        int borderPaddingStart = bv.getBorderAndPaddingWidthStart();
+        int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore();
+        float x, y;
+        x = (float)(bv.getXOffset() + containingIPPosition) / 1000f;
+        y = (float)(bv.getYOffset() + containingBPPosition) / 1000f;
+        //This is the content-rect
+        float width = (float)bv.getIPD() / 1000f;
+        float height = (float)bv.getBPD() / 1000f;
+
+
+        if (bv.getPositioning() == Block.ABSOLUTE
+                || bv.getPositioning() == Block.FIXED) {
+
+            currentIPPosition = bv.getXOffset();
+            currentBPPosition = bv.getYOffset();
+
+            //For FIXED, we need to break out of the current viewports to the
+            //one established by the page. We save the state stack for restoration
+            //after the block-container has been painted. See below.
+            List breakOutList = null;
+            if (bv.getPositioning() == Block.FIXED) {
+                breakOutList = breakOutOfStateStack();
+            }
+
+            CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
+            ctm = tempctm.multiply(ctm);
+
+            //Adjust for spaces (from margin or indirectly by start-indent etc.
+            x += bv.getSpaceStart() / 1000f;
+            currentIPPosition += bv.getSpaceStart();
+
+            y += bv.getSpaceBefore() / 1000f;
+            currentBPPosition += bv.getSpaceBefore();
+
+            float bpwidth = (borderPaddingStart + bv.getBorderAndPaddingWidthEnd()) / 1000f;
+            float bpheight = (borderPaddingBefore + bv.getBorderAndPaddingWidthAfter()) / 1000f;
+
+            drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight);
+
+            //Now adjust for border/padding
+            currentIPPosition += borderPaddingStart;
+            currentBPPosition += borderPaddingBefore;
+
+            Rectangle2D clippingRect = null;
+            clippingRect = new Rectangle(currentIPPosition, currentBPPosition,
+                    bv.getIPD(), bv.getBPD());
+
+            // startVParea(ctm, clippingRect);
+            pushViewPortPos(new ViewPortPos(clippingRect, ctm));
+            currentIPPosition = 0;
+            currentBPPosition = 0;
+            renderBlocks(bv, children);
+            //endVParea();
+            popViewPortPos();
+
+            if (breakOutList != null) {
+                restoreStateStackAfterBreakOut(breakOutList);
+            }
+
+            currentIPPosition = saveIP;
+            currentBPPosition = saveBP;
+        } else {
+
+            currentBPPosition += bv.getSpaceBefore();
+
+            //borders and background in the old coordinate system
+            handleBlockTraits(bv);
+
+            //Advance to start of content area
+            currentIPPosition += bv.getStartIndent();
+
+            CTM tempctm = new CTM(containingIPPosition, currentBPPosition);
+            ctm = tempctm.multiply(ctm);
+
+            //Now adjust for border/padding
+            currentBPPosition += borderPaddingBefore;
+
+            Rectangle2D clippingRect = null;
+            clippingRect = new Rectangle(currentIPPosition, currentBPPosition,
+                    bv.getIPD(), bv.getBPD());
+
+            //startVParea(ctm, clippingRect);
+            pushViewPortPos(new ViewPortPos(clippingRect, ctm));
+
+            currentIPPosition = 0;
+            currentBPPosition = 0;
+            renderBlocks(bv, children);
+            //endVParea();
+            popViewPortPos();
+
+            currentIPPosition = saveIP;
+            currentBPPosition = saveBP;
+
+            currentBPPosition += (int)(bv.getAllocBPD());
+        }
+        //currentFontName = saveFontName;
+    }
+
+    /**
+     * @see org.apache.fop.render.Renderer#renderPage(PageViewport)
+     */
+    public void renderPage(PageViewport page) {
+
+        // initializeRootExtensions(page);
+
+        _currentFontFamily = "";
+        _currentFontSize = 0;
+        _pageFontCounter = 0;
+        _currentPageFonts.clear();
+        _lineCache = new HashSet();
+
+        Rectangle2D bounds = page.getViewArea();
+
+        _pageWidth = mpts2units(bounds.getWidth());
+        _pageHeight = mpts2units(bounds.getHeight());
+
+        if (_pages != null && _pages.containsKey(page)) {
+
+            _afpDataStream.restorePage((PageObject)_pages.remove(page));
+
+        } else {
+            // renderPageGroupExtensions(page);
+
+            _afpDataStream.startPage(_pageWidth, _pageHeight, 0);
+
+            renderPageObjectExtensions(page);
+
+        }
+
+        pushViewPortPos(new ViewPortPos());
+
+        renderPageAreas(page.getPage());
+
+        Iterator i = _currentPageFonts.values().iterator();
+        while (i.hasNext()) {
+            AFPFontAttributes afpFontAttributes = (AFPFontAttributes) i.next();
+
+            _afpDataStream.createFont(
+                afpFontAttributes.getFontReference(),
+                afpFontAttributes.getFont(),
+                afpFontAttributes.getPointSize());
+
+        }
+
+        try {
+            _afpDataStream.endPage();
+        } catch (IOException ioex) {
+            // TODO What shall we do?
+        }
+
+        popViewPortPos();
+
+    }
+
+    /**
+     * Clip using the current path.
+     * @see org.apache.fop.render.AbstractRenderer#clip
+     */
+    public void clip() {
+        // TODO
+    }
+
+    /**
+     * Clip using a rectangular area.
+     * @see org.apache.fop.render.AbstractRenderer#clipRect(float, float, float, float)
+     */
+    public void clipRect(float x, float y, float width, float height) {
+        // TODO
+    }
+
+    /**
+     * Moves the current point to (x, y), omitting any connecting line segment.
+     * @see org.apache.fop.render.AbstractRenderer#moveTo(float, float)
+     */
+    public void moveTo(float x, float y) {
+        // TODO
+    }
+
+    /**
+     * Appends a straight line segment from the current point to (x, y). The
+     * new current point is (x, y).
+     * @see org.apache.fop.render.AbstractRenderer#lineTo(float, float)
+     */
+    public void lineTo(float x, float y) {
+        // TODO
+    }
+
+    /**
+     * Closes the current subpath by appending a straight line segment from
+     * the current point to the starting point of the subpath.
+     * @see org.apache.fop.render.AbstractRenderer#closePath
+     */
+    public void closePath() {
+        // TODO
+    }
+
+    /**
+     * Fill a rectangular area.
+     * @see org.apache.fop.render.AbstractRenderer#fillRect(float, float, float, float)
+     */
+    public void fillRect(float x, float y, float width, float height) {
+        /*
+        _afpDataStream.createShading(
+            pts2units(x),
+            pts2units(y),
+            pts2units(width),
+            pts2units(height),
+            _currentColor.getRed(),
+            _currentColor.getGreen(),
+            _currentColor.getBlue());
+         */
+        _afpDataStream.createLine(
+            pts2units(x),
+            pts2units(y),
+            pts2units(x + width),
+            pts2units(y),
+            pts2units(height),
+            _currentColor);
+    }
+
+    /**
+     * Draw a border segment of an XSL-FO style border.
+     * @see org.apache.fop.render.AbstractRenderer#drawBorderLine(float, float, float, float,
+     *       boolean, boolean, int, ColorType)
+     */
+    public void drawBorderLine(float x1, float y1, float x2, float y2,
+            boolean horz, boolean startOrBefore, int style, ColorType col) {
+        float w = x2 - x1;
+        float h = y2 - y1;
+        if ((w < 0) || (h < 0)) {
+            log.error("Negative extent received. Border won't be painted.");
+            return;
+        }
+        switch (style) {
+            case Constants.EN_DOUBLE:
+                if (horz) {
+                    float h3 = h / 3;
+                    float ym1 = y1;
+                    float ym2 = ym1 + h3 + h3;
+                    _afpDataStream.createLine(
+                        pts2units(x1),
+                        pts2units(ym1),
+                        pts2units(x2),
+                        pts2units(ym1),
+                        pts2units(h3),
+                        new AFPFontColor(col)
+                    );
+                    _afpDataStream.createLine(
+                        pts2units(x1),
+                        pts2units(ym2),
+                        pts2units(x2),
+                        pts2units(ym2),
+                        pts2units(h3),
+                        new AFPFontColor(col)
+                    );
+                } else {
+                    float w3 = w / 3;
+                    float xm1 = x1;
+                    float xm2 = xm1 + w3 + w3;
+                    _afpDataStream.createLine(
+                        pts2units(xm1),
+                        pts2units(y1),
+                        pts2units(xm1),
+                        pts2units(y2),
+                        pts2units(w3),
+                        new AFPFontColor(col)
+                    );
+                    _afpDataStream.createLine(
+                        pts2units(xm2),
+                        pts2units(y1),
+                        pts2units(xm2),
+                        pts2units(y2),
+                        pts2units(w3),
+                        new AFPFontColor(col)
+                    );
+                }
+                break;
+            case Constants.EN_DASHED:
+                if (horz) {
+                    float w2 = 2 * h;
+                    while (x1 + w2 < x2) {
+                        _afpDataStream.createLine(
+                            pts2units(x1),
+                            pts2units(y1),
+                            pts2units(x1 + w2),
+                            pts2units(y1),
+                            pts2units(h),
+                            new AFPFontColor(col)
+                        );
+                        x1 += 2 * w2;
+                    }
+                } else {
+                    float h2 = 2 * w;
+                    while (y1 + h2 < y2) {
+                        _afpDataStream.createLine(
+                            pts2units(x1),
+                            pts2units(y1),
+                            pts2units(x1),
+                            pts2units(y1 + h2),
+                            pts2units(w),
+                            new AFPFontColor(col)
+                        );
+                        y1 += 2 * h2;
+                    }
+                }
+                break;
+            case Constants.EN_DOTTED:
+                if (horz) {
+                    while (x1 + h < x2) {
+                        _afpDataStream.createLine(
+                            pts2units(x1),
+                            pts2units(y1),
+                            pts2units(x1 + h),
+                            pts2units(y1),
+                            pts2units(h),
+                            new AFPFontColor(col)
+                        );
+                        x1 += 2 * h;
+                    }
+                } else {
+                    while (y1 + w < y2) {
+                        _afpDataStream.createLine(
+                            pts2units(x1),
+                            pts2units(y1),
+                            pts2units(x1),
+                            pts2units(y1 + w),
+                            pts2units(w),
+                            new AFPFontColor(col)
+                        );
+                        y1 += 2 * w;
+                    }
+                }
+                break;
+            case Constants.EN_GROOVE:
+            case Constants.EN_RIDGE:
+            {
+                float colFactor = (style == EN_GROOVE ? 0.4f : -0.4f);
+                Color c = toColor(col);
+                if (horz) {
+                    Color uppercol = lightenColor(c, -colFactor);
+                    Color lowercol = lightenColor(c, colFactor);
+                    float h3 = h / 3;
+                    float ym1 = y1;
+                    _afpDataStream.createLine(
+                        pts2units(x1),
+                        pts2units(ym1),
+                        pts2units(x2),
+                        pts2units(ym1),
+                        pts2units(h3),
+                        new AFPFontColor(uppercol)
+                    );
+                    _afpDataStream.createLine(
+                        pts2units(x1),
+                        pts2units(ym1 + h3),
+                        pts2units(x2),
+                        pts2units(ym1 + h3),
+                        pts2units(h3),
+                        new AFPFontColor(c)
+                    );
+                    _afpDataStream.createLine(
+                        pts2units(x1),
+                        pts2units(ym1 + h3 + h3),
+                        pts2units(x2),
+                        pts2units(ym1 + h3 + h3),
+                        pts2units(h3),
+                        new AFPFontColor(lowercol)
+                    );
+                } else {
+                    Color leftcol = lightenColor(c, -colFactor);
+                    Color rightcol = lightenColor(c, colFactor);
+                    float w3 = w / 3;
+                    float xm1 = x1 + (w3 / 2);
+                    _afpDataStream.createLine(
+                        pts2units(xm1),
+                        pts2units(y1),
+                        pts2units(xm1),
+                        pts2units(y2),
+                        pts2units(w3),
+                        new AFPFontColor(leftcol)
+                    );
+                    _afpDataStream.createLine(
+                        pts2units(xm1 + w3),
+                        pts2units(y1),
+                        pts2units(xm1 + w3),
+                        pts2units(y2),
+                        pts2units(w3),
+                        new AFPFontColor(c)
+                    );
+                    _afpDataStream.createLine(
+                        pts2units(xm1 + w3 + w3),
+                        pts2units(y1),
+                        pts2units(xm1 + w3 + w3),
+                        pts2units(y2),
+                        pts2units(w3),
+                        new AFPFontColor(rightcol)
+                    );
+                }
+                break;
+            }
+            case Constants.EN_HIDDEN:
+                break;
+            case Constants.EN_INSET:
+            case Constants.EN_OUTSET:
+            default:
+                _afpDataStream.createLine(
+                    pts2units(x1),
+                    pts2units(y1),
+                    pts2units(horz ? x2 : x1),
+                    pts2units(horz ? y1 : y2),
+                    pts2units(Math.abs(horz ? (y2 - y1) : (x2 - x1))),
+                    new AFPFontColor(col));
+        }
+    }
+
+    /**
+     * Draw an image at the indicated location.
+     * @see org.apache.fop.render.AbstractRenderer#drawImage(String, Rectangle2D)
+     */
+    public void drawImage(String url, Rectangle2D pos) {
+        String name = null;
+        if (_pageSegmentsMap != null) {
+            name = (String)_pageSegmentsMap.get(url);
+        }
+        if (name != null) {
+            int x = mpts2units(pos.getX() + currentIPPosition);
+            int y = mpts2units(pos.getY() + currentBPPosition);
+            _afpDataStream.createIncludePageSegment(name, x, y);
+        } else {
+            url = ImageFactory.getURL(url);
+            ImageFactory fact = userAgent.getFactory().getImageFactory();
+            FopImage fopimage = fact.getImage(url, userAgent);
+            if (fopimage == null) {
+                return;
+            }
+            if (!fopimage.load(FopImage.DIMENSIONS)) {
+                return;
+            }
+            String mime = fopimage.getMimeType();
+            if ("text/xml".equals(mime)) {
+            } else if (MimeConstants.MIME_SVG.equals(mime)) {
+                if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
+                    return;
+                }
+                int x = mpts2units(pos.getX() + currentIPPosition);
+                int y = mpts2units(pos.getY() + currentBPPosition);
+                int w = mpts2units(pos.getWidth());
+                int h = mpts2units(pos.getHeight());
+                ImageObject io = _afpDataStream.getImageObject(x, y, w, h);
+                io.setImageParameters(
+                    (int)(fopimage.getHorizontalResolution() * 10),
+                    (int)(fopimage.getVerticalResolution() * 10),
+                    fopimage.getWidth(),
+                    fopimage.getHeight()
+                );
+                if (colorImages) {
+                    io.setImageIDESize((byte)24);
+                    io.setImageData(SVGConverter.convertToTIFF((XMLImage)fopimage));
+                } else {
+                    convertToGrayScaleImage(io, SVGConverter.convertToTIFF((XMLImage)fopimage));
+                }
+            } else if (MimeConstants.MIME_EPS.equals(mime)) {
+            /*
+            } else if (MimeConstants.MIME_JPEG.equals(mime)) {
+                if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
+                    return;
+                }
+                fact.releaseImage(url, userAgent);
+
+                int x = mpts2units(pos.getX() + currentIPPosition);
+                int y = mpts2units(pos.getY() + currentBPPosition);
+                int w = mpts2units(pos.getWidth());
+                int h = mpts2units(pos.getHeight());
+                ImageObject io = _afpDataStream.getImageObject();
+                io.setImageViewport(x, y, w, h);
+                io.setImageParameters(
+                    (int)(fopimage.getHorizontalResolution() * 10),
+                    (int)(fopimage.getVerticalResolution() * 10),
+                    fopimage.getWidth(),
+                    fopimage.getHeight()
+                );
+                io.setImageIDESize((byte)fopimage.getBitsPerPixel());
+                io.setImageEncoding((byte)0x83);
+                io.setImageData(fopimage.getRessourceBytes());
+            */
+            } else if (MimeConstants.MIME_TIFF.equals(mime)
+                        && fopimage instanceof TIFFImage) {
+                TIFFImage tiffImage = (TIFFImage) fopimage;
+                int x = mpts2units(pos.getX() + currentIPPosition);
+                int y = mpts2units(pos.getY() + currentBPPosition);
+                int w = mpts2units(pos.getWidth());
+                int h = mpts2units(pos.getHeight());
+                ImageObject io = _afpDataStream.getImageObject(x, y, w, h);
+                io.setImageParameters(
+                    (int)(fopimage.getHorizontalResolution() * 10),
+                    (int)(fopimage.getVerticalResolution() * 10),
+                    fopimage.getWidth(),
+                    fopimage.getHeight()
+                );
+                if (tiffImage.getStripCount() == 1) {
+                    int comp = tiffImage.getCompression();
+                    if (comp == 3) {
+                        if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
+                            return;
+                        }
+                        io.setImageEncoding((byte)0x81);
+                        io.setImageData(fopimage.getRessourceBytes());
+                    } else if (comp == 4) {
+                        if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
+                            return;
+                        }
+                        io.setImageEncoding((byte)0x82);
+                        io.setImageData(fopimage.getRessourceBytes());
+                    } else {
+                        if (!fopimage.load(FopImage.BITMAP)) {
+                            return;
+                        }
+                        convertToGrayScaleImage(io, fopimage.getBitmaps());
+                    }
+                }
+                else
+                {
+                    if (!fopimage.load(FopImage.BITMAP)) {
+                        return;
+                    }
+                    convertToGrayScaleImage(io, fopimage.getBitmaps());
+                }
+            } else {
+                if (!fopimage.load(FopImage.BITMAP)) {
+                    return;
+                }
+                fact.releaseImage(url, userAgent);
+
+                int x = mpts2units(pos.getX() + currentIPPosition);
+                int y = mpts2units(pos.getY() + currentBPPosition);
+                int w = mpts2units(pos.getWidth());
+                int h = mpts2units(pos.getHeight());
+                ImageObject io = _afpDataStream.getImageObject(x, y, w, h);
+                io.setImageParameters(
+                    (int)(fopimage.getHorizontalResolution() * 10),
+                    (int)(fopimage.getVerticalResolution() * 10),
+                    fopimage.getWidth(),
+                    fopimage.getHeight()
+                );
+                if (colorImages) {
+                    io.setImageIDESize((byte)24);
+                    io.setImageData(fopimage.getBitmaps());
+                } else {
+                    convertToGrayScaleImage(io, fopimage.getBitmaps());
+                }
+            }
+        }
+    }
+
+    /**
+     * Establishes a new foreground or fill color.
+     * @see org.apache.fop.render.AbstractRenderer#updateColor(ColorType, boolean)
+     */
+    public void updateColor(ColorType col, boolean fill) {
+        if (fill) {
+            _currentColor = new AFPFontColor(col);
+        }
+    }
+
+    /**
+     * Restores the state stack after a break out.
+     * @param breakOutList the state stack to restore.
+     */
+    public void restoreStateStackAfterBreakOut(List breakOutList) {
+
+    }
+
+    /**
+     * Breaks out of the state stack to handle fixed block-containers.
+     * @return the saved state stack to recreate later
+     */
+    public List breakOutOfStateStack() {
+        return null;
+    }
+
+    /** Saves the graphics state of the rendering engine. */
+    public void saveGraphicsState() {
+
+    }
+
+    /** Restores the last graphics state of the rendering engine. */
+    public void restoreGraphicsState() {
+
+    }
+
+    /** Indicates the beginning of a text object. */
+    public void beginTextObject() {
+
+    }
+
+    /** Indicates the end of a text object. */
+    public void endTextObject() {
+
+    }
+
+    /**
+     * @see org.apache.fop.render.AbstractRenderer#renderImage(Image, Rectangle2D)
+     */
+    public void renderImage(Image image, Rectangle2D pos) {
+        String url = image.getURL();
+        drawImage(url, pos);
+    }
+
+    /**
+     * @see org.apache.fop.render.AbstractRenderer#renderCharacter(Character)
+     */
+    public void renderCharacter(Character ch) {
+        renderInlineAreaBackAndBorders(ch);
+
+        String name = getInternalFontNameForArea(ch);
+        _currentFontSize = ((Integer) ch.getTrait(Trait.FONT_SIZE)).intValue();
+        AFPFont tf = (AFPFont) fontInfo.getFonts().get(name);
+        _currentFontFamily = name;
+
+        ColorType ct = (ColorType) ch.getTrait(Trait.COLOR);
+
+        int vsci = mpts2units(tf.getWidth(' ', _currentFontSize) / 1000
+                                + ch.getTextWordSpaceAdjust()
+                                + ch.getTextLetterSpaceAdjust());
+
+        // word.getOffset() = only height of text itself
+        // currentBlockIPPosition: 0 for beginning of line; nonzero
+        //  where previous line area failed to take up entire allocated space
+        int rx = currentIPPosition + ch.getBorderAndPaddingWidthStart();
+        int bl = currentBPPosition + ch.getOffset() + ch.getBaselineOffset();
+
+        // Set letterSpacing
+        //float ls = fs.getLetterSpacing() / this.currentFontSize;
+
+        String worddata = ch.getChar();
+
+        // Create an AFPFontAttributes object from the current font details
+        AFPFontAttributes afpFontAttributes =
+            new AFPFontAttributes(name, tf,_currentFontSize);
+
+        if (!_currentPageFonts.containsKey(afpFontAttributes.getFontKey())) {
+            // Font not found on current page, so add the new one
+            _pageFontCounter++;
+            afpFontAttributes.setFontReference(_pageFontCounter);
+            _currentPageFonts.put(
+                afpFontAttributes.getFontKey(),
+                afpFontAttributes);
+
+        } else {
+            // Use the previously stored font attributes
+            afpFontAttributes =
+                (AFPFontAttributes) _currentPageFonts.get(afpFontAttributes.getFontKey());
+
+        }
+
+        // Try and get the encoding to use for the font
+        String encoding = null;
+
+        try {
+            encoding = tf.getCharacterSet(_currentFontSize).getEncoding();
+        } catch (Throwable ex) {
+            encoding = AFPConstants.EBCIDIC_ENCODING;
+            log.warn(
+                "renderTextArea():: Error getting encoding for font "
+                + " - using default encoding "
+                + encoding);
+        }
+
+        try {
+            _afpDataStream.createText(
+                afpFontAttributes.getFontReference(),
+                mpts2units(rx),
+                mpts2units(bl),
+                new AFPFontColor(ct),
+                vsci,
+                mpts2units(ch.getTextLetterSpaceAdjust()),
+                worddata.getBytes(encoding));
+
+        } catch (UnsupportedEncodingException usee) {
+            log.error(
+                "renderWordArea:: Font "
+                + afpFontAttributes.getFontKey()
+                + " caused UnsupportedEncodingException");
+
+        }
+
+        super.renderCharacter(ch);
+
+        renderTextDecoration(tf, _currentFontSize, ch, bl, rx);
+
+    }
+
+    /**
+     * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea)
+     */
+    public void renderText(TextArea text) {
+        renderInlineAreaBackAndBorders(text);
+
+        String name = getInternalFontNameForArea(text);
+        _currentFontSize = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
+        AFPFont tf = (AFPFont) fontInfo.getFonts().get(name);
+
+        ColorType ct = (ColorType) text.getTrait(Trait.COLOR);
+
+        int vsci = mpts2units(tf.getWidth(' ', _currentFontSize) / 1000
+                                + text.getTextWordSpaceAdjust()
+                                + text.getTextLetterSpaceAdjust());
+
+        // word.getOffset() = only height of text itself
+        // currentBlockIPPosition: 0 for beginning of line; nonzero
+        //  where previous line area failed to take up entire allocated space
+        int rx = currentIPPosition + text.getBorderAndPaddingWidthStart();
+        int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset();
+
+        // Set letterSpacing
+        //float ls = fs.getLetterSpacing() / this.currentFontSize;
+
+        String worddata = text.getText();
+
+        // Create an AFPFontAttributes object from the current font details
+        AFPFontAttributes afpFontAttributes =
+            new AFPFontAttributes(name, tf, _currentFontSize);
+
+        if (!_currentPageFonts.containsKey(afpFontAttributes.getFontKey())) {
+            // Font not found on current page, so add the new one
+            _pageFontCounter++;
+            afpFontAttributes.setFontReference(_pageFontCounter);
+            _currentPageFonts.put(
+                afpFontAttributes.getFontKey(),
+                afpFontAttributes);
+
+        } else {
+            // Use the previously stored font attributes
+            afpFontAttributes =
+                (AFPFontAttributes) _currentPageFonts.get(
+                afpFontAttributes.getFontKey());
+        }
+
+        // Try and get the encoding to use for the font
+        String encoding = null;
+
+        try {
+            encoding = tf.getCharacterSet(_currentFontSize).getEncoding();
+        } catch (Throwable ex) {
+            encoding = AFPConstants.EBCIDIC_ENCODING;
+            log.warn(
+                "renderText():: Error getting encoding for font "
+                + " - using default encoding "
+                + encoding);
+        }
+
+        try {
+            _afpDataStream.createText(
+                afpFontAttributes.getFontReference(),
+                mpts2units(rx),
+                mpts2units(bl),
+                new AFPFontColor(ct),
+                vsci,
+                mpts2units(text.getTextLetterSpaceAdjust()),
+                worddata.getBytes(encoding));
+        } catch (UnsupportedEncodingException usee) {
+            log.error(
+                "renderText:: Font "
+                + afpFontAttributes.getFontKey()
+                + " caused UnsupportedEncodingException");
+        }
+
+        super.renderText(text);
+
+        renderTextDecoration(tf, _currentFontSize, text, bl, rx);
+    }
+
+    /**
+     * @see org.apache.fop.render.AbstractRenderer#renderWord(WordArea)
+     */
+    public void renderWord(WordArea word) {
+        String name = getInternalFontNameForArea(word.getParentArea());
+        int size = ((Integer) word.getParentArea().getTrait(Trait.FONT_SIZE)).intValue();
+        AFPFont tf = (AFPFont) fontInfo.getFonts().get(name);
+
+        String s = word.getWord();
+
+        FontMetrics metrics = fontInfo.getMetricsFor(name);
+
+        super.renderWord(word);
+    }
+
+    /**
+     * @see org.apache.fop.render.AbstractRenderer#renderSpace(SpaceArea)
+     */
+    public void renderSpace(SpaceArea space) {
+        String name = getInternalFontNameForArea(space.getParentArea());
+        int size = ((Integer) space.getParentArea().getTrait(Trait.FONT_SIZE)).intValue();
+        AFPFont tf = (AFPFont) fontInfo.getFonts().get(name);
+
+        String s = space.getSpace();
+
+        FontMetrics metrics = fontInfo.getMetricsFor(name);
+
+        super.renderSpace(space);
+    }
+
+    /**
+     * Render leader area.
+     * This renders a leader area which is an area with a rule.
+     * @param area the leader area to render
+     */
+    public void renderLeader(Leader area) {
+        renderInlineAreaBackAndBorders(area);
+
+        int style = area.getRuleStyle();
+        float startx = (currentIPPosition + area.getBorderAndPaddingWidthStart()) / 1000f;
+        float starty = (currentBPPosition + area.getOffset()) / 1000f;
+        float endx = (currentIPPosition + area.getBorderAndPaddingWidthStart()
+                        + area.getIPD()) / 1000f;
+        float ruleThickness = area.getRuleThickness() / 1000f;
+        ColorType col = (ColorType)area.getTrait(Trait.COLOR);
+
+        switch (style) {
+            case EN_SOLID:
+            case EN_DASHED:
+            case EN_DOUBLE:
+            case EN_DOTTED:
+            case EN_GROOVE:
+            case EN_RIDGE:
+                drawBorderLine(startx, starty, endx, starty + ruleThickness,
+                        true, true, style, col);
+                break;
+            default:
+                throw new UnsupportedOperationException("rule style not supported");
+        }
+        super.renderLeader(area);
+    }
+
+    /**
+     * @see org.apache.fop.render.Renderer#setProducer(String)
+     */
+    public void setProducer(String producer) {
+        _afpDataStream.setProducer(producer);
+    }
+
+    /**
+     * @see org.apache.fop.render.Renderer#setOptions(Map)
+     */
+    public void setOptions(Map options) {
+
+        _afpOptions = options;
+
+    }
+
+    /**
+     * Determines the orientation from the string representation, this method
+     * guarantees to return a value of either 0, 90, 180 or 270.
+     *
+     * @return the orientation
+     */
+    private int getOrientation(String orientationString) {
+
+        int orientation = 0;
+        if (orientationString != null && orientationString.length() > 0) {
+            try {
+                orientation = Integer.parseInt(orientationString);
+            } catch (NumberFormatException nfe) {
+                log.error(
+                    "Cannot use orientation of "
+                    + orientation
+                    + " defaulting to zero.");
+                orientation = 0;
+            }
+        } else {
+            orientation = 0;
+        }
+        switch (orientation) {
+            case 0 :
+                break;
+            case 90 :
+                break;
+            case 180 :
+                break;
+            case 270 :
+                break;
+            default :
+                log.error(
+                    "Cannot use orientation of "
+                    + orientation
+                    + " defaulting to zero.");
+                orientation = 0;
+                break;
+        }
+
+        return orientation;
+
+    }
+
+    /**
+     * Sets the rotation to be used for portrait pages, valid values are 0
+     * (default), 90, 180, 270.
+     *
+     * @param rotation
+     *            The rotation in degrees.
+     */
+    public void setPortraitRotation(int rotation) {
+
+        if (rotation == 0
+            || rotation == 90
+            || rotation == 180
+            || rotation == 270) {
+            _portraitRotation = rotation;
+        } else {
+            throw new IllegalArgumentException("The portrait rotation must be one of the values 0, 90, 180, 270");
+
+        }
+
+    }
+
+    /**
+     * Sets the rotation to be used for landsacpe pages, valid values are 0, 90,
+     * 180, 270 (default).
+     *
+     * @param rotation
+     *            The rotation in degrees.
+     */
+    public void setLandscapeRotation(int rotation) {
+
+        if (rotation == 0
+            || rotation == 90
+            || rotation == 180
+            || rotation == 270) {
+            _landscapeRotation = rotation;
+        } else {
+            throw new IllegalArgumentException("The landscape rotation must be one of the values 0, 90, 180, 270");
+
+        }
+
+    }
+
+    /**
+     * Get the MIME type of the renderer.
+     *
+     * @return   The MIME type of the renderer
+     */
+    public String getMimeType() {
+        return MimeConstants.MIME_AFP;
+    }
+
+    /**
+     * Method to render the page extension.
+     * <p>
+     *
+     * @param page
+     *            the page object
+     */
+    private void renderPageObjectExtensions(PageViewport page) {
+
+        _pageSegmentsMap = null;
+        if (page.getExtensionAttachments().size() > 0) {
+            //Extract all AFPPageSetup instances from the attachment list on the s-p-m
+            Iterator i = page.getExtensionAttachments().iterator();
+            while (i.hasNext()) {
+                ExtensionAttachment attachment = (ExtensionAttachment)i.next();
+                if (AFPPageSetup.CATEGORY.equals(attachment.getCategory())) {
+                    AFPPageSetup aps = (AFPPageSetup)attachment;
+                    String element = aps.getElementName();
+                    if (AFPElementMapping.INCLUDE_PAGE_OVERLAY.equals(element)) { 
+                        String overlay = aps.getName();
+                        if (overlay != null) {
+                            _afpDataStream.createIncludePageOverlay(overlay);
+                        }
+                    } else if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(element)) { 
+                        String name = aps.getName();
+                        String source = aps.getValue();
+                        if (_pageSegmentsMap == null) {
+                            _pageSegmentsMap = new HashMap();
+                        }
+                        _pageSegmentsMap.put(source, name);
+                    } else if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(element)) { 
+                        String name = aps.getName();
+                        String value = aps.getValue();
+                        if (_pageSegmentsMap == null) {
+                            _pageSegmentsMap = new HashMap();
+                        }
+                        _afpDataStream.createTagLogicalElement(name, value);
+                    }
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Converts FOP mpt measurement to afp measurement units
+     * @param mpt the millipoints value
+     */
+    private int mpts2units(int mpt) {
+        return mpts2units((double) mpt);
+    }
+
+    /**
+     * Converts FOP pt measurement to afp measurement units
+     * @param mpt the millipoints value
+     */
+    private int pts2units(float mpt) {
+        return mpts2units(mpt * 1000d);
+    }
+
+    /**
+     * Converts FOP mpt measurement to afp measurement units
+     * @param mpt the millipoints value
+     */
+    private int mpts2units(double mpt) {
+        return (int)Math.round(mpt / DPI_CONVERSION_FACTOR_240);
+    }
+
+    private void convertToGrayScaleImage(ImageObject io, byte raw[]) {
+        int pixelsPerByte = 8 / bitsPerPixel;
+        byte bw[] = new byte[raw.length / (3 * pixelsPerByte)];
+        int k = 0;
+        for (int i = 0, j = 0; i < raw.length; i += 3, j++) {
+            if (j == pixelsPerByte) {
+                j = 0;
+                k++;
+            }
+            // see http://www.jguru.com/faq/view.jsp?EID=221919
+            double greyVal = 0.212671d * ((int) raw[i] & 0xff)
+                + 0.715160d * ((int) raw[i + 1] & 0xff)
+                + 0.072169d * ((int) raw[i + 2] & 0xff);
+            switch (bitsPerPixel) {
+                case 1:
+                    if (greyVal > 128) {
+                        bw[k] |= (byte)(1 << j);
+                    }
+                    break;
+                case 4:
+                    greyVal /= 16;
+                    bw[k] |= (byte)((byte)greyVal << (j * 4));
+                    break;
+                case 8:
+                    bw[k] = (byte)greyVal;
+                    break;
+            }
+        }
+        io.setImageIDESize((byte)bitsPerPixel);
+        io.setImageData(bw);
+    }
+
+    private class ViewPortPos {
+        int x = 0;
+        int y = 0;
+        int rot = 0;
+
+        ViewPortPos() {
+        }
+
+        ViewPortPos(Rectangle2D view, CTM ctm) {
+            ViewPortPos currentVP = (ViewPortPos)viewPortPositions.get(viewPortPositions.size()-1);
+            int xOrigin;
+            int yOrigin;
+            int width;
+            int height;
+            switch (currentVP.rot) {
+                case 90:
+                    width = mpts2units(view.getHeight());;
+                    height = mpts2units(view.getWidth());
+                    xOrigin = _pageWidth - width - mpts2units(view.getY()) - currentVP.y;
+                    yOrigin = mpts2units(view.getX()) + currentVP.x;
+                    break;
+                case 180:
+                    width = mpts2units(view.getWidth());
+                    height = mpts2units(view.getHeight());;
+                    xOrigin = _pageWidth - width - mpts2units(view.getX()) - currentVP.x;
+                    yOrigin = _pageHeight - height - mpts2units(view.getY()) - currentVP.y;
+                    break;
+                case 270:
+                    width = mpts2units(view.getHeight());
+                    height = mpts2units(view.getWidth());
+                    xOrigin = mpts2units(view.getY()) + currentVP.y;
+                    yOrigin = _pageHeight - height - mpts2units(view.getX()) - currentVP.x;
+                    break;
+                default:
+                    xOrigin = mpts2units(view.getX()) + currentVP.x;
+                    yOrigin = mpts2units(view.getY()) + currentVP.y;
+                    width = mpts2units(view.getWidth());
+                    height = mpts2units(view.getHeight());
+                    break;
+            }
+            this.rot = currentVP.rot;
+            double ctmf[] = ctm.toArray();
+            if (ctmf[0] == 0.0d && ctmf[1] == -1.0d && ctmf[2] == 1.0d && ctmf[3] == 0.d) {
+                this.rot += 270;
+            } else if (ctmf[0] == -1.0d && ctmf[1] == 0.0d && ctmf[2] == 0.0d && ctmf[3] == -1.0d) {
+                this.rot += 180;
+            } else if (ctmf[0] == 0.0d && ctmf[1] == 1.0d && ctmf[2] == -1.0d && ctmf[3] == 0.0d) {
+                this.rot += 90;
+            }
+            this.rot %= 360;
+            switch (this.rot) {
+                /*
+                case 0:
+                    this.x = mpts2units(view.getX()) + x;
+                    this.y = mpts2units(view.getY()) + y;
+                    break;
+                case 90:
+                    this.x = mpts2units(view.getY()) + y;
+                    this.y = _pageWidth - mpts2units(view.getX() + view.getWidth()) - x;
+                    break;
+                case 180:
+                    this.x = _pageWidth - mpts2units(view.getX() + view.getWidth()) - x;
+                    this.y = _pageHeight - mpts2units(view.getY() + view.getHeight()) - y;
+                    break;
+                case 270:
+                    this.x = _pageHeight - mpts2units(view.getY() + view.getHeight()) - y;
+                    this.y = mpts2units(view.getX()) + x;
+                    break;
+                 */
+                case 0:
+                    this.x = xOrigin;
+                    this.y = yOrigin;
+                    break;
+                case 90:
+                    this.x = yOrigin;
+                    this.y = _pageWidth - width - xOrigin;
+                    break;
+                case 180:
+                    this.x = _pageWidth - width - xOrigin;
+                    this.y = _pageHeight - height - yOrigin;
+                    break;
+                case 270:
+                    this.x = _pageHeight - height - yOrigin;
+                    this.y = xOrigin;
+                    break;
+            }
+        }
+
+        public String toString() {
+            return "x:" + x + " y:" + y + " rot:" + rot;
+        }
+
+    }
+
+    private List viewPortPositions = new ArrayList();
+
+    private void pushViewPortPos(ViewPortPos vpp) {
+        viewPortPositions.add(vpp);
+        _afpDataStream.setOffsets(vpp.x, vpp.y, vpp.rot);
+    }
+
+    private void popViewPortPos() {
+        viewPortPositions.remove(viewPortPositions.size() - 1);
+        if (viewPortPositions.size() > 0) {
+            ViewPortPos vpp = (ViewPortPos)viewPortPositions.get(viewPortPositions.size() - 1);
+            _afpDataStream.setOffsets(vpp.x, vpp.y, vpp.rot);
+        }
+    }
+
+}
+
diff --git a/src/sandbox/org/apache/fop/render/afp/AFPRendererMaker.java b/src/sandbox/org/apache/fop/render/afp/AFPRendererMaker.java
new file mode 100644 (file)
index 0000000..54715f2
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.render.AbstractRendererMaker;
+import org.apache.fop.render.Renderer;
+
+/**
+ * RendererMaker for the AFP Renderer.
+ */
+public class AFPRendererMaker extends AbstractRendererMaker {
+
+    private static final String[] MIMES = new String[] {
+        MimeConstants.MIME_AFP,
+        MimeConstants.MIME_AFP_ALT};
+
+
+    /**@see org.apache.fop.render.AbstractRendererMaker */
+    public Renderer makeRenderer(FOUserAgent ua) {
+        return new AFPRenderer();
+    }
+
+    /** @see org.apache.fop.render.AbstractRendererMaker#needsOutputStream() */
+    public boolean needsOutputStream() {
+        return true;
+    }
+
+    /** @see org.apache.fop.render.AbstractRendererMaker#getSupportedMimeTypes() */
+    public String[] getSupportedMimeTypes() {
+        return MIMES;
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/exceptions/FontRuntimeException.java b/src/sandbox/org/apache/fop/render/afp/exceptions/FontRuntimeException.java
new file mode 100644 (file)
index 0000000..ea692b1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.exceptions;
+
+/**
+ * A runtime exception for handling fatal errors in processing fonts.
+ * <p/>
+ */
+public class FontRuntimeException extends NestedRuntimeException {
+
+    /**
+     * Constructs a FontRuntimeException with the specified message.
+     * @param msg the exception mesaage
+     */
+    public FontRuntimeException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs a FontRuntimeException with the specified message
+     * wrapping the underlying exception.
+     * @param msg the exception mesaage
+     * @param t the underlying exception
+     */
+    public FontRuntimeException(String msg, Throwable t) {
+        super(msg, t);
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/exceptions/NestedRuntimeException.java b/src/sandbox/org/apache/fop/render/afp/exceptions/NestedRuntimeException.java
new file mode 100644 (file)
index 0000000..92254e6
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.exceptions;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Handy class for wrapping runtime Exceptions with a root cause.
+ * This technique is no longer necessary in Java 1.4, which provides
+ * built-in support for exception nesting. Thus exceptions in applications
+ * written to use Java 1.4 need not extend this class.
+ *
+ */
+public abstract class NestedRuntimeException extends RuntimeException {
+
+       /** Root cause of this nested exception */
+       private Throwable _underlyingException;
+
+       /**
+        * Construct a <code>NestedRuntimeException</code> with the specified detail message.
+        * @param msg The detail message.
+        */
+       public NestedRuntimeException(String msg) {
+               super(msg);
+       }
+
+       /**
+        * Construct a <code>NestedRuntimeException</code> with the specified
+        * detail message and nested exception.
+        * @param msg The detail message.
+        * @param t The nested exception.
+        */
+       public NestedRuntimeException(String msg, Throwable t) {
+               super(msg);
+               _underlyingException = t;
+
+       }
+
+       /**
+        * Gets the original triggering exception
+        * @return The original exception as a throwable.
+        */
+       public Throwable getUnderlyingException() {
+
+               return _underlyingException;
+
+       }
+
+       /**
+        * Return the detail message, including the message from the nested
+        * exception if there is one.
+        * @return The detail message.
+        */
+       public String getMessage() {
+
+               if (_underlyingException == null) {
+                       return super.getMessage();
+               } else {
+                       return super.getMessage()
+                               + "; nested exception is "
+                               + _underlyingException.getClass().getName();
+               }
+
+       }
+
+       /**
+        * Print the composite message and the embedded stack trace to the specified stream.
+        * @param ps the print stream
+        */
+       public void printStackTrace(PrintStream ps) {
+               if (_underlyingException == null) {
+                       super.printStackTrace(ps);
+               } else {
+                       ps.println(this);
+                       _underlyingException.printStackTrace(ps);
+               }
+       }
+
+       /**
+        * Print the composite message and the embedded stack trace to the specified writer.
+        * @param pw the print writer
+        */
+       public void printStackTrace(PrintWriter pw) {
+               if (_underlyingException == null) {
+                       super.printStackTrace(pw);
+               } else {
+                       pw.println(this);
+                       _underlyingException.printStackTrace(pw);
+               }
+       }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/exceptions/RendererRuntimeException.java b/src/sandbox/org/apache/fop/render/afp/exceptions/RendererRuntimeException.java
new file mode 100644 (file)
index 0000000..a3be63e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.exceptions;
+
+/**
+ * A runtime exception for handling fatal errors in rendering.
+ * <p/>
+ */
+public class RendererRuntimeException extends NestedRuntimeException {
+
+       /**
+        * Constructs a RendererRuntimeException with the specified message.
+        * @param msg the exception mesaage
+        */
+       public RendererRuntimeException(String msg) {
+               super(msg);
+       }
+
+       /**
+        * Constructs a RendererRuntimeException with the specified message
+        * wrapping the underlying exception.
+        * @param msg the exception mesaage
+        * @param t the underlying exception
+        */
+       public RendererRuntimeException(String msg, Throwable t) {
+               super(msg, t);
+       }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/extensions/AFPAttribute.java b/src/sandbox/org/apache/fop/render/afp/extensions/AFPAttribute.java
new file mode 100755 (executable)
index 0000000..a1c6385
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.properties.Property;
+import org.apache.fop.fo.properties.StringProperty;
+
+/**
+ * This class extends the org.apache.fop.fo.StringProperty.Maker inner class
+ * in order to provide a static property maker. The object faciliates
+ * extraction of attributes from formatted objects based on the static list
+ * as defined in the AFPElementMapping implementation.
+ * <p/>
+ */
+public class AFPAttribute extends StringProperty.Maker {
+
+    /**
+     * The attribute property.
+     */
+    private Property _property;
+
+    /**
+     * Constructor for the AFPAttribute.
+     * @param name The attribute name
+     */
+    protected AFPAttribute(String name) {
+        super(0);
+        _property = null;
+    }
+
+    /**
+     * Overide the make method to return the property object
+     * @return property The property object.
+     */
+    public Property make(PropertyList propertyList) {
+        if (_property == null) {
+            _property = make(propertyList, "", propertyList.getParentFObj());
+        }
+        return _property;
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/extensions/AFPElementMapping.java b/src/sandbox/org/apache/fop/render/afp/extensions/AFPElementMapping.java
new file mode 100755 (executable)
index 0000000..350b75c
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+import java.util.HashMap;
+
+import org.apache.fop.fo.ElementMapping;
+import org.apache.fop.fo.FONode;
+
+
+/**
+ * AFPElementMapping object provides the ability to extract information
+ * from the formatted object that reside in the afp namespace. This is used
+ * for custom AFP extensions not supported by the FO schema. Examples include
+ * adding overlays or indexing a document using the tag logical element
+ * structured field.
+ * <p/>
+ */
+public class AFPElementMapping extends ElementMapping {
+
+    public static final String PAGE = "page";
+
+    public static final String PAGE_GROUP = "page-group";
+
+    public static final String TAG_LOGICAL_ELEMENT = "tag-logical-element";
+
+    public static final String INCLUDE_PAGE_OVERLAY = "include-page-overlay";
+
+    public static final String INCLUDE_PAGE_SEGMENT = "include-page-segment";
+
+    /**
+     * The namespace used for AFP extensions
+     */
+    public static final String NAMESPACE = "http://org.apache.fop/extensions/afp";
+
+    /**
+     * The usual namespace prefix used for AFP extensions
+     */
+    public static final String NAMESPACE_PREFIX = "afp";
+
+    /** Main constructor */
+    public AFPElementMapping() {
+        this.namespaceURI = NAMESPACE;
+    }
+
+    /**
+     * Private static synchronized method to set up the element and atribute
+     * HashMaps, this defines what elements and attributes are extracted.
+     */
+    protected void initialize() {
+
+        if (foObjs == null) {
+            foObjs = new HashMap();
+            foObjs.put(PAGE, new AFPPageSetupMaker());
+            // foObjs.put(PAGE_GROUP, new AFPMaker());
+            foObjs.put(
+                TAG_LOGICAL_ELEMENT,
+                new AFPTagLogicalElementMaker());
+            foObjs.put(
+                INCLUDE_PAGE_SEGMENT,
+                new AFPIncludePageSegmentMaker());
+            foObjs.put(
+                INCLUDE_PAGE_OVERLAY,
+                new AFPIncludePageOverlayMaker());
+        }
+
+    }
+
+    static class AFPPageSetupMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new AFPPageSetupElement(parent);
+        }
+    }
+
+    static class AFPIncludePageOverlayMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new AFPElement(parent, INCLUDE_PAGE_OVERLAY);
+        }
+    }
+
+    static class AFPIncludePageSegmentMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new AFPElement(parent, INCLUDE_PAGE_SEGMENT);
+        }
+    }
+
+    static class AFPTagLogicalElementMaker extends ElementMapping.Maker {
+        public FONode make(FONode parent) {
+            return new AFPElement(parent, TAG_LOGICAL_ELEMENT);
+        }
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/extensions/AFPPageSetup.java b/src/sandbox/org/apache/fop/render/afp/extensions/AFPPageSetup.java
new file mode 100644 (file)
index 0000000..d95a3a4
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+import java.io.Serializable;
+
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+
+/**
+ * This is the pass-through value object for the PostScript extension.
+ */
+public class AFPPageSetup implements ExtensionAttachment, Serializable {
+
+    /** The category URI for this extension attachment. */
+    public static final String CATEGORY = "apache:fop:extensions:afp";
+
+    private String elementName;
+
+    private String name;
+
+    private String value;
+
+    /**
+     * Default constructor.
+     * @param name the name of the setup code object, may be null
+     */
+    public AFPPageSetup(String name) {
+        this.elementName = name;
+    }
+
+    /** @return the name */
+    public String getElementName() {
+        return elementName;
+    }
+
+    /** @return the name */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets the name of the setup code object.
+     * @param name The name to set.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * @return the value 
+     */
+    public String getValue() {
+        return value;
+    }
+
+    /**
+     * Sets the value
+     * @param value The value name to set.
+     */
+    public void setValue(String source) {
+        this.value = source;
+    }
+
+    /** @see org.apache.fop.fo.extensions.ExtensionAttachment#getCategory() */
+    public String getCategory() {
+        return CATEGORY;
+    }
+
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        return "AFPPageSetup(element-name=" + getElementName() + " name=" + getName() + ")";
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java b/src/sandbox/org/apache/fop/render/afp/extensions/AFPPageSetupElement.java
new file mode 100644 (file)
index 0000000..cbdde91
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.ValidationException;
+
+/**
+ * Extension element for fox:ps-page-setup-code.
+ */
+public class AFPPageSetupElement extends AbstractAFPExtensionObject {
+
+    /**
+     * Main constructor
+     * @param parent parent FO node
+     */
+    public AFPPageSetupElement(FONode parent) {
+        super(parent, "page");
+    }
+
+    /** @see org.apache.fop.fo.FONode#startOfNode() */
+    protected void startOfNode() throws FOPException {
+        super.startOfNode();
+        if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) {
+            throw new ValidationException(getName() + " must be a child of fo:simple-page-master.");
+        }
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/extensions/AbstractAFPExtensionObject.java b/src/sandbox/org/apache/fop/render/afp/extensions/AbstractAFPExtensionObject.java
new file mode 100644 (file)
index 0000000..b8dc7c7
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+// FOP
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.ValidationException;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+/**
+ * Base class for the AFP-specific extension elements.
+ */
+public abstract class AbstractAFPExtensionObject extends FONode {
+
+    private AFPPageSetup setupCode = null;
+
+    private String _name = null;
+    
+    /**
+     * @see org.apache.fop.fo.FONode#FONode(FONode)
+     * @param parent the parent formatting object
+     * @param name the name of the afp element
+     */
+    public AbstractAFPExtensionObject(FONode parent, String name) {
+        super(parent);
+        _name = name;
+        setupCode = new AFPPageSetup(name);
+    }
+
+    /**
+     * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String)
+     * here, blocks XSL FO's from having non-FO parents.
+     */
+    protected void validateChildNode(Locator loc, String nsURI, String localName)
+                throws ValidationException {
+        if (FO_URI.equals(nsURI)) {
+            invalidChildError(loc, nsURI, localName);
+        }
+    }
+
+    /** @see org.apache.fop.fo.FONode */
+    protected void addCharacters(char[] data, int start, int length,
+                                 PropertyList pList, Locator locator) {
+    }
+
+    /** @see org.apache.fop.fo.FONode#getNamespaceURI() */
+    public String getNamespaceURI() {
+        return AFPElementMapping.NAMESPACE;
+    }
+
+    /**@see org.apache.fop.fo.FONode#getNormalNamespacePrefix() */
+    public String getNormalNamespacePrefix() {
+        return AFPElementMapping.NAMESPACE_PREFIX;
+    }
+
+    /** @see org.apache.fop.fo.FONode#processNode */
+    public void processNode(String elementName, Locator locator,
+                            Attributes attlist, PropertyList propertyList)
+                                throws FOPException {
+        String name = attlist.getValue("name");
+        if (name != null && name.length() > 0) {
+            setupCode.setName(name);
+        } else {
+            throw new FOPException(elementName + " must have a name attribute.");
+        }
+        if (AFPElementMapping.INCLUDE_PAGE_SEGMENT.equals(elementName)) {
+            name = attlist.getValue("src");
+            if (name != null && name.length() > 0) {
+                setupCode.setValue(name);
+            } else {
+                throw new FOPException(elementName + " must have a src attribute.");
+            }
+        }
+        if (AFPElementMapping.TAG_LOGICAL_ELEMENT.equals(elementName)) {
+            name = attlist.getValue("value");
+            if (name != null && name.length() > 0) {
+                setupCode.setValue(name);
+            } else {
+                throw new FOPException(elementName + " must have a value attribute.");
+            }
+        }
+    }
+
+    /** @see org.apache.fop.fo.FONode#endOfNode() */
+    protected void endOfNode() throws FOPException {
+        super.endOfNode();
+    }
+
+    /** @see org.apache.fop.fo.FONode#getExtensionAttachment() */
+    public ExtensionAttachment getExtensionAttachment() {
+        return this.setupCode;
+    }
+
+    /** @see org.apache.fop.fo.FONode#getLocalName() */
+    public String getLocalName() {
+        return _name;
+    }
+
+}
+
diff --git a/src/sandbox/org/apache/fop/render/afp/fonts/AFPFont.java b/src/sandbox/org/apache/fop/render/afp/fonts/AFPFont.java
new file mode 100644 (file)
index 0000000..d17536a
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fonts;
+import java.util.Map;
+import org.apache.fop.fonts.FontType;
+import org.apache.fop.fonts.Typeface;
+
+
+/**
+ * All implemenations of AFP fonts should extend this base class,
+ * the object implements the FontMetrics information.
+ * <p/>
+ */
+public abstract class AFPFont extends Typeface {
+
+    /** The font name */
+    protected String _name;
+
+    /**
+     * Constructor for the base font requires the name.
+     * @param name the name of the font
+     */
+    public AFPFont(String name) {
+
+        _name = name;
+
+    }
+
+    /**
+     * @return the name of the font.
+     */
+    public String getFontName() {
+        return _name;
+    }
+
+    /**
+     * Returns the type of the font.
+     * @return the font type
+     */
+    public FontType getFontType() {
+        return FontType.OTHER;
+    }
+
+    /**
+     * Indicates if the font has kering information.
+     * @return True, if kerning is available.
+     */
+    public boolean hasKerningInfo() {
+        return false;
+    }
+
+    /**
+     * Returns the kerning map for the font.
+     * @return the kerning map
+     */
+    public Map getKerningInfo() {
+        return null;
+    }
+
+    /**
+     * Returns the character set for a given size
+     * @param size the font size
+     */
+    public abstract CharacterSet getCharacterSet(int size);
+
+     /**
+     * Determines whether this font contains a particular character/glyph.
+     * @param c character to check
+     * @return True if the character is supported, Falso otherwise
+     */
+    public boolean hasChar(char c) {
+        return true;
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/fonts/AFPFontInfo.java b/src/sandbox/org/apache/fop/render/afp/fonts/AFPFontInfo.java
new file mode 100644 (file)
index 0000000..ce08d41
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fonts;
+
+import java.util.List;
+
+/**
+ * FontInfo contains meta information on fonts
+ */
+public class AFPFontInfo {
+
+    private AFPFont font;
+    private List fontTriplets;
+
+    /**
+     * Main constructor
+     * @param afpFont The AFP Font
+     * @param fontTriplets List of font triplets to associate with this font
+     */
+    public AFPFontInfo(AFPFont afpFont, List fontTriplets) {
+        this.font = afpFont;
+        this.fontTriplets = fontTriplets;
+    }
+
+    /**
+     * Returns the afp font
+     * @return the afp font
+     */
+    public AFPFont getAFPFont() {
+        return font;
+    }
+
+    /**
+     * Returns the list of font triplets associated with this font.
+     * @return List of font triplets
+     */
+    public List getFontTriplets() {
+        return fontTriplets;
+    }
+
+}
+
diff --git a/src/sandbox/org/apache/fop/render/afp/fonts/AFPFontReader.java b/src/sandbox/org/apache/fop/render/afp/fonts/AFPFontReader.java
new file mode 100644 (file)
index 0000000..cb8718d
--- /dev/null
@@ -0,0 +1,608 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fonts;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.render.afp.exceptions.FontRuntimeException;
+import org.apache.fop.render.afp.modca.AFPConstants;
+import org.apache.fop.render.afp.tools.StructuredFieldReader;
+
+/**
+ * The AFPFontReader is responsible for reading the font attributes from binary
+ * code page files and the character set metric files. In IBM font structure, a
+ * code page maps each character of text to the characters in a character set.
+ * Each character is translated into a code point. When the character is
+ * printed, each code point is matched to a character ID on the code page
+ * specified. The character ID is then matched to the image (raster pattern or
+ * outline pattern) of the character in the character set specified. The image
+ * in the character set is the image that is printed in the document. To be a
+ * valid code page for a particular character set, all character IDs in the code
+ * page must be included in that character set. <p/>This class will read the
+ * font information from the binary code page files and character set metric
+ * files in order to determine the correct metrics to use when rendering the
+ * formatted object. <p/>
+ *
+ * @author <a href="mailto:pete@townsend.uk.com">Pete Townsend </a>
+ */
+public final class AFPFontReader {
+
+    /**
+     * Static logging instance
+     */
+    protected static final Log log = LogFactory.getLog("org.apache.fop.render.afp.fonts");
+
+    /**
+     * Template used to convert lists to arrays.
+     */
+    private static final CharacterSetOrientation[] EMPTY_CSO_ARRAY = new CharacterSetOrientation[0];
+
+    /** Codepage MO:DCA structured field. */
+    private static final byte[] CODEPAGE_SF = new byte[] { (byte) 0xD3,
+        (byte) 0xA8, (byte) 0x87 };
+
+    /** Character table MO:DCA structured field. */
+    private static final byte[] CHARACTER_TABLE_SF = new byte[] { (byte) 0xD3,
+        (byte) 0x8C, (byte) 0x87 };
+
+    /** Font control MO:DCA structured field. */
+    private static final byte[] FONT_CONTROL_SF = new byte[] { (byte) 0xD3,
+        (byte) 0xA7, (byte) 0x89 };
+
+    /** Font orientation MO:DCA structured field. */
+    private static final byte[] FONT_ORIENTATION_SF = new byte[] { (byte) 0xD3,
+        (byte) 0xAE, (byte) 0x89 };
+
+    /** Font position MO:DCA structured field. */
+    private static final byte[] FONT_POSITION_SF = new byte[] { (byte) 0xD3,
+        (byte) 0xAC, (byte) 0x89 };
+
+    /** Font index MO:DCA structured field. */
+    private static final byte[] FONT_INDEX_SF = new byte[] { (byte) 0xD3,
+        (byte) 0x8C, (byte) 0x89 };
+
+    /**
+     * The conversion factor to millipoints for 240 dpi
+     */
+    private static final int FOP_100_DPI_FACTOR = 1;
+
+    /**
+     * The conversion factor to millipoints for 240 dpi
+     */
+    private static final int FOP_240_DPI_FACTOR = 300000;
+
+    /**
+     * The conversion factor to millipoints for 300 dpi
+     */
+    private static final int FOP_300_DPI_FACTOR = 240000;
+
+    /**
+     * The encoding to use to convert from EBCIDIC to ASCII
+     */
+    private static final String ASCII_ENCODING = "UTF8";
+
+    /**
+     * The collection of code pages
+     */
+    private static HashMap _codePages = new HashMap();
+
+    /**
+     * Load the font details and metrics into the CharacterSetMetric object,
+     * this will use the actual afp code page and character set files to load
+     * the object with the necessary metrics.
+     *
+     * @param characterSet the CharacterSetMetric object to populate
+     */
+    public static void loadCharacterSetMetric(CharacterSet characterSet) {
+
+        InputStream inputStream = null;
+
+        try {
+
+            /**
+             * Get the code page which contains the character mapping
+             * information to map the unicode character id to the graphic
+             * chracter global identifier.
+             */
+            String cp = new String(characterSet.getCodePage());
+            String path = characterSet.getPath();
+
+            HashMap codepage = (HashMap) _codePages.get(cp);
+
+            if (codepage == null) {
+                codepage = loadCodePage(cp, characterSet.getEncoding(), path);
+                _codePages.put(cp, codepage);
+            }
+
+            /**
+             * Load the character set metric information, no need to cache this
+             * information as it should be cached by the objects that wish to
+             * load character set metric information.
+             */
+            final String characterset = characterSet.getName();
+
+            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+            if (classLoader == null) {
+                classLoader = AFPFontReader.class.getClassLoader();
+            }
+
+            URL url = classLoader.getResource(path);
+            if (url == null) {
+                try {
+                    File file = new File(path);
+                    url = file.toURL();
+                    if (url == null) {
+                        String msg = "CharacterSet file not found for "
+                            + characterset + " in classpath: " + path;
+                        log.error(msg);
+                        throw new FileNotFoundException(msg);
+                    }
+                } catch (MalformedURLException ex) {
+                    String msg = "CharacterSet file not found for "
+                        + characterset + " in classpath: " + path;
+                    log.error(msg);
+                    throw new FileNotFoundException(msg);
+                }
+
+            }
+
+            File directory = new File(url.getPath());
+
+            final String filterpattern = characterset.trim();
+            FilenameFilter filter = new FilenameFilter() {
+                public boolean accept(File dir, String name) {
+                    return name.startsWith(filterpattern);
+                }
+            };
+
+            File[] csfont = directory.listFiles(filter);
+            if (csfont.length < 1) {
+                String msg = "CharacterSet file search for " + characterset
+                    + " located " + csfont.length + " files";
+                log.error(msg);
+                throw new FileNotFoundException(msg);
+            } else if (csfont.length > 1) {
+                String msg = "CharacterSet file search for " + characterset
+                    + " located " + csfont.length + " files";
+                log.warn(msg);
+            }
+
+            inputStream = inputStream = csfont[0].toURL().openStream();
+            if (inputStream == null) {
+                String msg = "Failed to open character set resource "
+                    + characterset;
+                log.error(msg);
+                throw new FileNotFoundException(msg);
+            }
+
+            StructuredFieldReader sfr = new StructuredFieldReader(inputStream);
+
+            // Process D3A789 Font Control
+            FontControl fnc = processFontControl(sfr);
+
+            //process D3AE89 Font Orientation
+            CharacterSetOrientation[] csoArray = processFontOrientation(sfr);
+
+            //process D3AC89 Font Position
+            processFontPosition(sfr, csoArray, fnc.getDpi());
+
+            //process D38C89 Font Index (per orientation)
+            for (int i = 0; i < csoArray.length; i++) {
+                processFontIndex(sfr, csoArray[i], codepage, fnc.getDpi());
+                characterSet.addCharacterSetOrientation(csoArray[i]);
+            }
+
+        } catch (Exception ex) {
+            throw new FontRuntimeException(
+                "Failed to load the character set metrics for code page "
+                + characterSet.getCodePage(), ex);
+        } finally {
+            try {
+                inputStream.close();
+            } catch (Exception ex) {
+                // Ignore
+            }
+        }
+
+    }
+
+    /**
+     * Load the code page information from the appropriate file. The file name
+     * to load is determined by the code page name and the file extension 'CDP'.
+     *
+     * @param codePage
+     *            the code page identifier
+     * @param encoding
+     *            the encoding to use for the character decoding
+     */
+    private static HashMap loadCodePage(String codePage, String encoding,
+        String path) throws IOException, FileNotFoundException {
+
+        // Create the HashMap to store code page information
+        HashMap codepages = new HashMap();
+
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        if (classLoader == null) {
+            classLoader = AFPFontReader.class.getClassLoader();
+        }
+
+        URL url = classLoader.getResource(path);
+
+        if (url == null) {
+            try {
+                File file = new File(path);
+                url = file.toURL();
+                if (url == null) {
+                    String msg = "CodePage file not found for " + codePage
+                        + " in classpath: " + path;
+                    log.error(msg);
+                    throw new FileNotFoundException(msg);
+                }
+            } catch (MalformedURLException ex) {
+                String msg = "CodePage file not found for " + codePage
+                    + " in classpath: " + path;
+                log.error(msg);
+                throw new FileNotFoundException(msg);
+            }
+
+        }
+
+        File directory = new File(url.getPath());
+
+        final String filterpattern = codePage.trim();
+        FilenameFilter filter = new FilenameFilter() {
+            public boolean accept(File dir, String name) {
+                return name.startsWith(filterpattern);
+            }
+        };
+
+        File[] codepage = directory.listFiles(filter);
+
+        if (codepage.length < 1) {
+            String msg = "CodePage file search for " + codePage + " located "
+                + codepage.length + " files";
+            log.error(msg);
+            throw new FileNotFoundException(msg);
+        } else if (codepage.length > 1) {
+            String msg = "CodePage file search for " + codePage + " located "
+                + codepage.length + " files";
+            log.warn(msg);
+        }
+
+        InputStream is = codepage[0].toURL().openStream();
+
+        if (is == null) {
+            String msg = "AFPFontReader:: loadCodePage(String):: code page file not found for "
+                + codePage;
+            log.error(msg);
+            throw new FileNotFoundException(msg);
+        }
+
+        StructuredFieldReader sfr = new StructuredFieldReader(is);
+        byte[] data = sfr.getNext(CHARACTER_TABLE_SF);
+
+        int position = 0;
+        byte[] gcgiBytes = new byte[8];
+        byte[] charBytes = new byte[1];
+
+        // Read data, ignoring bytes 0 - 2
+        for (int index = 3; index < data.length; index++) {
+            if (position < 8) {
+                // Build the graphic character global identifier key
+                gcgiBytes[position] = data[index];
+                position++;
+            } else if (position == 9) {
+                position = 0;
+                // Set the character
+                charBytes[0] = data[index];
+                String gcgiString = new String(gcgiBytes,
+                    AFPConstants.EBCIDIC_ENCODING);
+                String charString = new String(charBytes, encoding);
+                int value = charString.charAt(0);
+                codepages.put(gcgiString, charString);
+            } else {
+                position++;
+            }
+        }
+
+        try {
+            is.close();
+        } catch (Exception ex) {
+            // Ignore
+        }
+
+        return codepages;
+
+    }
+
+    /**
+     * Process the font control details using the structured field reader.
+     *
+     * @param sfr
+     *            the structured field reader
+     */
+    private static FontControl processFontControl(StructuredFieldReader sfr)
+    throws IOException {
+
+        byte[] fncData = sfr.getNext(FONT_CONTROL_SF);
+
+        int position = 0;
+
+        FontControl fontControl = new AFPFontReader().new FontControl();
+
+        if (fncData[7] == (byte) 0x02) {
+            fontControl.setRelative(true);
+        }
+
+        int dpi = (((fncData[9] & 0xFF) << 8) + (fncData[10] & 0xFF)) / 10;
+
+        fontControl.setDpi(dpi);
+
+        return fontControl;
+
+    }
+
+    /**
+     * Process the font orientation details from using the structured field
+     * reader.
+     *
+     * @param sfr
+     *            the structured field reader
+     */
+    private static CharacterSetOrientation[] processFontOrientation(
+        StructuredFieldReader sfr) throws IOException {
+
+        byte[] data = sfr.getNext(FONT_ORIENTATION_SF);
+
+        int position = 0;
+        byte[] fnoData = new byte[26];
+
+        ArrayList orientations = new ArrayList();
+
+        // Read data, ignoring bytes 0 - 2
+        for (int index = 3; index < data.length; index++) {
+            // Build the font orientation record
+            fnoData[position] = data[index];
+            position++;
+
+            if (position == 26) {
+
+                position = 0;
+
+                int orientation = 0;
+
+                switch (fnoData[2]) {
+                    case 0x00:
+                        orientation = 0;
+                        break;
+                    case 0x2D:
+                        orientation = 90;
+                        break;
+                    case 0x5A:
+                        orientation = 180;
+                        break;
+                    case (byte) 0x87:
+                        orientation = 270;
+                        break;
+                    default:
+                        System.out.println("ERROR: Oriantation");
+                }
+
+                CharacterSetOrientation cso = new CharacterSetOrientation(
+                    orientation);
+                orientations.add(cso);
+
+            }
+        }
+
+        return (CharacterSetOrientation[]) orientations
+            .toArray(EMPTY_CSO_ARRAY);
+    }
+
+    /**
+     * Populate the CharacterSetOrientation object in the suplied array with the
+     * font position details using the supplied structured field reader.
+     *
+     * @param sfr
+     *            the structured field reader
+     * @param csoArray
+     *            the array of CharacterSetOrientation objects
+     */
+    private static void processFontPosition(StructuredFieldReader sfr,
+        CharacterSetOrientation[] csoArray, int dpi) throws IOException {
+
+        byte[] data = sfr.getNext(FONT_POSITION_SF);
+
+        int position = 0;
+        byte[] fpData = new byte[26];
+
+        int csoIndex = 0;
+        int fopFactor = 0;
+
+        switch (dpi) {
+            case 100:
+                fopFactor = FOP_100_DPI_FACTOR;
+                break;
+            case 240:
+                fopFactor = FOP_240_DPI_FACTOR;
+                break;
+            case 300:
+                fopFactor = FOP_300_DPI_FACTOR;
+                break;
+            default:
+                String msg = "Unsupported font resolution of " + dpi + " dpi.";
+                log.error(msg);
+                throw new IOException(msg);
+        }
+
+        // Read data, ignoring bytes 0 - 2
+        for (int index = 3; index < data.length; index++) {
+            if (position < 22) {
+                // Build the font orientation record
+                fpData[position] = data[index];
+            } else if (position == 22) {
+
+                position = 0;
+
+                CharacterSetOrientation cso = csoArray[csoIndex];
+
+                int xHeight = ((fpData[2] & 0xFF) << 8) + (fpData[3] & 0xFF);
+                int capHeight = ((fpData[4] & 0xFF) << 8) + (fpData[5] & 0xFF);
+                int ascHeight = ((fpData[6] & 0xFF) << 8) + (fpData[7] & 0xFF);
+                int dscHeight = ((fpData[8] & 0xFF) << 8) + (fpData[9] & 0xFF);
+
+                dscHeight = dscHeight * -1;
+
+                cso.setXHeight(xHeight * fopFactor);
+                cso.setCapHeight(capHeight * fopFactor);
+                cso.setAscender(ascHeight * fopFactor);
+                cso.setDescender(dscHeight * fopFactor);
+
+                csoIndex++;
+
+                fpData[position] = data[index];
+
+            }
+
+            position++;
+        }
+
+    }
+
+    /**
+     * Process the font index details for the character set orientation.
+     *
+     * @param sfr
+     *            the structured field reader
+     * @param cso
+     *            the CharacterSetOrientation object to populate
+     * @param codepage
+     *            the map of code pages
+     */
+    private static void processFontIndex(StructuredFieldReader sfr,
+        CharacterSetOrientation cso, HashMap codepage, int dpi)
+        throws IOException {
+
+        byte[] data = sfr.getNext(FONT_INDEX_SF);
+
+        int fopFactor = 0;
+
+        switch (dpi) {
+            case 100:
+                fopFactor = FOP_100_DPI_FACTOR;
+                break;
+            case 240:
+                fopFactor = FOP_240_DPI_FACTOR;
+                break;
+            case 300:
+                fopFactor = FOP_300_DPI_FACTOR;
+                break;
+            default:
+                String msg = "Unsupported font resolution of " + dpi + " dpi.";
+                log.error(msg);
+                throw new IOException(msg);
+        }
+
+        int position = 0;
+
+        byte[] gcgid = new byte[8];
+        byte[] fiData = new byte[20];
+
+        int lowest = 255;
+        int highest = 0;
+
+        // Read data, ignoring bytes 0 - 2
+        for (int index = 3; index < data.length; index++) {
+            if (position < 8) {
+                gcgid[position] = (byte) data[index];
+                position++;
+            } else if (position < 27) {
+                fiData[position - 8] = (byte) data[index];
+                position++;
+            } else if (position == 27) {
+
+                fiData[position - 8] = (byte) data[index];
+
+                position = 0;
+
+                String gcgiString = new String(gcgid, AFPConstants.EBCIDIC_ENCODING);
+
+                String idx = (String) codepage.get(gcgiString);
+
+                if (idx != null) {
+
+                    int cidx = idx.charAt(0);
+                    int width = ((fiData[0] & 0xFF) << 8) + (fiData[1] & 0xFF);
+
+                    if (cidx < lowest) {
+                        lowest = cidx;
+                    }
+
+                    if (cidx > highest) {
+                        highest = cidx;
+                    }
+
+                    int a = (width * fopFactor);
+
+                    cso.setWidth(cidx, a);
+
+                }
+
+            }
+        }
+
+        cso.setFirstChar(lowest);
+        cso.setLastChar(highest);
+
+    }
+
+    private class FontControl {
+
+        private int _dpi;
+
+        private boolean isRelative = false;
+
+        public int getDpi() {
+            return _dpi;
+        }
+
+        public void setDpi(int i) {
+            _dpi = i;
+        }
+
+        public boolean isRelative() {
+            return isRelative;
+        }
+
+        public void setRelative(boolean b) {
+            isRelative = b;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/fonts/CharacterSet.java b/src/sandbox/org/apache/fop/render/afp/fonts/CharacterSet.java
new file mode 100644 (file)
index 0000000..ac39eff
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fonts;
+
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.render.afp.modca.AFPConstants;
+import org.apache.fop.render.afp.tools.StringUtils;
+
+/**
+ * The IBM Font Object Content Architecture (FOCA) supports presentation
+ * of character shapes by defining their characteristics, which include
+ * font description information for identifying the characters, font metric
+ * information for positioning the characters, and character shape information
+ * for presenting the character images.
+ * <p/>
+ * Presenting a graphic character on a presentation surface requires
+ * information on the rotation and position of character on the physical
+ * or logical page.
+ * <p/>
+ * This class proivdes font metric information for a particular font
+ * as identified by the character set name. This information is obtained
+ * directly from the AFP font files which must be installed in the path
+ * specified in the afp-fonts xml definition file.
+ * <p/>
+ */
+public class CharacterSet {
+
+    /**
+     * Static logging instance
+     */
+    protected static final Log log = LogFactory.getLog(CharacterSet.class.getName());
+
+    /**
+     * The code page to which the character set relates
+     */
+    protected String _codePage;
+
+    /**
+     * The encoding used for the code page
+     */
+    protected String _encoding;
+
+    /**
+     * The character set relating to the font
+     */
+    protected String _name;
+
+    /**
+     * The name of the character set as EBCIDIC bytes
+     */
+    private byte[] _nameBytes;
+
+    /**
+     * The path to the installed fonts
+     */
+    protected String _path;
+
+    /**
+     * Indicator as to whether to metrics have been loaded
+     */
+    private boolean _isMetricsLoaded = false;
+
+    /**
+     * The current orientation (currently only 0 is suppoted by FOP)
+     */
+    private String _currentOrientation = "0";
+
+    /**
+     * The collection of objects for each orientation
+     */
+    private HashMap _characterSetOrientations;
+
+    /**
+     * Constructor for the CharacterSetMetric object, the character set is used
+     * to load the font information from the actual AFP font.
+     * @param codePage the code page identifier
+     * @param encoding the encoding of the font
+     * @param name the character set name
+     * @param path the path to the installed afp fonts
+     */
+    public CharacterSet(
+        String codePage,
+        String encoding,
+        String name,
+        String path) {
+
+        if (name.length() > 8) {
+            String msg = "Character set name must be a maximum of 8 characters " + name;
+            log.error("Constructor:: " + msg);
+            throw new IllegalArgumentException(msg);
+        }
+
+        if (name.length() < 8) {
+            _name = StringUtils.rpad(name, ' ', 8);
+        } else {
+            _name = name;
+        }
+
+        try {
+
+            _nameBytes = name.getBytes(AFPConstants.EBCIDIC_ENCODING);
+
+        } catch (UnsupportedEncodingException usee) {
+
+            _nameBytes = name.getBytes();
+            log.warn(
+                "Constructor:: UnsupportedEncodingException translating the name "
+                + name);
+
+        }
+
+        _codePage = codePage;
+        _encoding = encoding;
+        _path = path;
+        _characterSetOrientations = new HashMap(4);
+
+    }
+
+    /**
+     * Add character set metric information for the different orientations
+     * @param cso the metrics for the orientation
+     */
+    public void addCharacterSetOrientation(CharacterSetOrientation cso) {
+
+        _characterSetOrientations.put(
+            String.valueOf(cso.getOrientation()),
+            cso);
+
+    }
+
+    /**
+     * Ascender height is the distance from the character baseline to the
+     * top of the character box. A negative ascender height signifies that
+     * all of the graphic character is below the character baseline. For
+     * a character rotation other than 0, ascender height loses its
+     * meaning when the character is lying on its side or is upside down
+     * with respect to normal viewing orientation. For the general case,
+     * Ascender Height is the character�s most positive y-axis value.
+     * For bounded character boxes, for a given character having an
+     * ascender, ascender height and baseline offset are equal.
+     * @return the ascender value in millipoints
+     */
+    public int getAscender() {
+        load();
+        return getCharacterSetOrientation().getAscender();
+    }
+
+    /**
+     * Cap height is the average height of the uppercase characters in
+     * a font. This value is specified by the designer of a font and is
+     * usually the height of the uppercase M.
+     * @return the cap height value in millipoints
+     */
+    public int getCapHeight() {
+        load();
+        return getCharacterSetOrientation().getCapHeight();
+    }
+
+    /**
+     * Descender depth is the distance from the character baseline to
+     * the bottom of a character box. A negative descender depth signifies
+     * that all of the graphic character is above the character baseline.
+     * @return the descender value in millipoints
+     */
+    public int getDescender() {
+        load();
+        return getCharacterSetOrientation().getDescender();
+    }
+
+    /**
+     * The first character in the character set
+     * @return the first character
+     */
+    public int getFirstChar() {
+        load();
+        return getCharacterSetOrientation().getFirstChar();
+    }
+
+    /**
+     * The last character in the character set
+     * @return the last character
+     */
+    public int getLastChar() {
+        load();
+        return getCharacterSetOrientation().getLastChar();
+    }
+
+    /**
+     * @return the path where the font resources are installed
+     */
+    public String getPath() {
+        return _path;
+    }
+
+    /**
+     * Get the width (in 1/1000ths of a point size) of all characters
+     * @return the widths of all characters
+     */
+    public int[] getWidths() {
+        load();
+        return getCharacterSetOrientation().getWidths();
+    }
+
+    /**
+     * XHeight refers to the height of the lower case letters above the baseline.
+     * @return the typical height of characters
+     */
+    public int getXHeight() {
+        load();
+        return getCharacterSetOrientation().getXHeight();
+    }
+
+    /**
+     * Get the width (in 1/1000ths of a point size) of the character
+     * identified by the parameter passed.
+     * @param character the character from which the width will be calculated
+     * @return the width of the character
+     */
+    public int width(int character) {
+        load();
+        return getCharacterSetOrientation().width(character);
+    }
+
+    /**
+     * Lazy creation of the character metrics, the afp font file will only
+     * be processed on a method call requiring the metric information.
+     */
+    private void load() {
+
+        if (!_isMetricsLoaded) {
+
+            AFPFontReader.loadCharacterSetMetric(this);
+            _isMetricsLoaded = true;
+
+        }
+
+    }
+
+    /**
+     * Returns the AFP character set identifier
+     * @return String
+     */
+    public String getName() {
+        return _name;
+    }
+
+    /**
+     * Returns the AFP character set identifier
+     * @return byte[]
+     */
+    public byte[] getNameBytes() {
+        return _nameBytes;
+    }
+
+    /**
+     * Returns the AFP code page identifier
+     * @return String
+     */
+    public String getCodePage() {
+        return _codePage;
+    }
+
+    /**
+     * Returns the AFP code page encoding
+     * @return String
+     */
+    public String getEncoding() {
+        return _encoding;
+    }
+
+    /**
+     * Helper method to return the current CharacterSetOrientation, note
+     * that FOP does not yet implement the "reference-orientation"
+     * attribute therefore we always use the orientation zero degrees,
+     * Other orientation information is captured for use by a future
+     * implementation (whenever FOP implement the mechanism). This is also
+     * the case for landscape prints which use an orientation of 270 degrees,
+     * in 99.9% of cases the font metrics will be the same as the 0 degrees
+     * therefore the implementation currely will always use 0 degrees.
+     * @return characterSetOrentation The current orientation metrics.
+     */
+    private CharacterSetOrientation getCharacterSetOrientation() {
+
+        CharacterSetOrientation c =
+            (CharacterSetOrientation) _characterSetOrientations.get(
+            _currentOrientation);
+        return c;
+
+    }
+
+    /**
+     * Map a Unicode character to a code point in the font.
+     * The code tables are already converted to Unicode therefore
+     * we can use the identity mapping.
+     * @param c character to map
+     * @return the mapped character
+     */
+    public char mapChar(char c) {
+        return c;
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/fonts/CharacterSetOrientation.java b/src/sandbox/org/apache/fop/render/afp/fonts/CharacterSetOrientation.java
new file mode 100644 (file)
index 0000000..68eb2e4
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fonts;
+
+
+/**
+ * The IBM Font Object Content Architecture (FOCA) supports presentation
+ * of character shapes by defining their characteristics, which include
+ * Font-Description information for identifying the characters, Font-Metric
+ * information for positioning the characters, and Character-Shape
+ * information for presenting the character images.
+ *
+ * Presenting a graphic character on a presentation surface requires
+ * that you communicate this information clearly to rotate and position
+ * characters correctly on the physical or logical page.
+ *
+ * This class proivdes font metric information for a particular font
+ * as by the orientation.
+ *
+ * This informtaion is obtained directly from the AFP font files which must
+ * be installed in the classpath under in the location specified by the path
+ * attribute in the afp-font.xml file.
+ * <p/>
+ */
+public class CharacterSetOrientation {
+
+    /**
+     * The code page to which the character set relates
+     */
+    private String _codePage;
+
+    /**
+     * The encoding used for the code page
+     */
+    private String _encoding;
+
+    /**
+     * The ascender height for the character set
+     */
+    private int _ascender;
+
+    /**
+     * The descender depth for the character set
+     */
+    private int _descender;
+
+    /**
+     * The height of capital letters
+     */
+    private int _capHeight;
+
+    /**
+     * The characters in the charcater set
+     */
+    private int[] _characters = new int[256];
+
+    /**
+     * The height of lowercase letters
+     */
+    private int _xHeight;
+
+    /**
+     * The first character
+     */
+    private int _firstCharacter;
+
+    /**
+     * The last character
+     */
+    private int _lastCharacter;
+
+
+    /**
+     * The character set orientation
+     */
+    private int _orientation = 0;
+
+    /**
+     * Constructor for the CharacterSetOrientation, the orientation is
+     * expressed as the degrees rotation (i.e 0, 90, 180, 270)
+     * @param orientation the character set orientation
+     */
+    public CharacterSetOrientation(int orientation) {
+
+        _orientation = orientation;
+
+    }
+
+    /**
+     * Ascender height is the distance from the character baseline to the
+     * top of the character box. A negative ascender height signifies that
+     * all of the graphic character is below the character baseline. For
+     * a character rotation other than 0, ascender height loses its
+     * meaning when the character is lying on its side or is upside down
+     * with respect to normal viewing orientation. For the general case,
+     * Ascender Height is the character�s most positive y-axis value.
+     * For bounded character boxes, for a given character having an
+     * ascender, ascender height and baseline offset are equal.
+     * @return the ascender value in millipoints
+     */
+    public int getAscender() {
+        return _ascender;
+    }
+
+    /**
+     * Cap height is the average height of the uppercase characters in
+     * a font. This value is specified by the designer of a font and is
+     * usually the height of the uppercase M.
+     * @return the cap height value in millipoints
+     */
+    public int getCapHeight() {
+        return _capHeight;
+    }
+
+    /**
+     * Descender depth is the distance from the character baseline to
+     * the bottom of a character box. A negative descender depth signifies
+     * that all of the graphic character is above the character baseline.
+     * @return the descender value in millipoints
+     */
+    public int getDescender() {
+        return _descender;
+    }
+
+    /**
+     * The first character in the character set
+     * @return the first character
+     */
+    public int getFirstChar() {
+        return _firstCharacter;
+    }
+
+    /**
+     * The last character in the character set
+     * @return the last character
+     */
+    public int getLastChar() {
+        return _lastCharacter;
+    }
+
+    /**
+     * The orientation for these metrics in the character set
+     * @return the orientation
+     */
+    public int getOrientation() {
+        return _orientation;
+    }
+
+    /**
+     * Get the width (in 1/1000ths of a point size) of all characters
+     * in this character set.
+     * @return the widths of all characters
+     */
+    public int[] getWidths() {
+
+        int arr[] = new int[(getLastChar() - getFirstChar()) + 1];
+        System.arraycopy(_characters, getFirstChar(), arr, 0, (getLastChar() - getFirstChar()) + 1);
+        return arr;
+
+    }
+
+    /**
+     * XHeight refers to the height of the lower case letters above
+     * the baseline.
+     * @return heightX the typical height of characters
+     */
+    public int getXHeight() {
+        return _xHeight;
+    }
+
+    /**
+     * Get the width (in 1/1000ths of a point size) of the character
+     * identified by the parameter passed.
+     * @param character the character to evaluate
+     * @return the widths of the character
+     */
+    public int width(int character) {
+        return _characters[character];
+    }
+
+    /**
+     * Ascender height is the distance from the character baseline to the
+     * top of the character box. A negative ascender height signifies that
+     * all of the graphic character is below the character baseline. For
+     * a character rotation other than 0, ascender height loses its
+     * meaning when the character is lying on its side or is upside down
+     * with respect to normal viewing orientation. For the general case,
+     * Ascender Height is the character�s most positive y-axis value.
+     * For bounded character boxes, for a given character having an
+     * ascender, ascender height and baseline offset are equal.
+     * @param ascender the ascender to set
+     */
+    public void setAscender(int ascender) {
+        _ascender = ascender;
+    }
+
+    /**
+     * Cap height is the average height of the uppercase characters in
+     * a font. This value is specified by the designer of a font and is
+     * usually the height of the uppercase M.
+     * @param capHeight the cap height to set
+     */
+    public void setCapHeight(int capHeight) {
+        _capHeight = capHeight;
+    }
+
+    /**
+     * Descender depth is the distance from the character baseline to
+     * the bottom of a character box. A negative descender depth signifies
+     * that all of the graphic character is above the character baseline.
+     * @param descender        the descender value in millipoints
+     */
+    public void setDescender(int descender) {
+        _descender = descender;
+    }
+
+    /**
+     * The first character in the character set
+     * @param firstCharacter the first character
+     */
+    public void setFirstChar(int firstCharacter) {
+        _firstCharacter = firstCharacter;
+    }
+
+    /**
+     * The last character in the character set
+     * @param lastCharacter the last character
+     */
+    public void setLastChar(int lastCharacter) {
+        _lastCharacter = lastCharacter;
+    }
+
+    /**
+     * Set the width (in 1/1000ths of a point size) of the character
+     * identified by the parameter passed.
+     * @param character the character for which the width is being set
+     * @param width    the widths of the character
+     */
+    public void setWidth(int character, int width) {
+
+        if (character >= _characters.length) {
+            // Increase the size of the array if necessary
+            int arr[] = new int[(character - _firstCharacter) + 1];
+            System.arraycopy(_characters, 0, arr, 0, _characters.length);
+            _characters = arr;
+        }
+        _characters[character] = width;
+
+    }
+
+    /**
+     * XHeight refers to the height of the lower case letters above
+     * the baseline.
+     * @param xHeight the typical height of characters
+     */
+    public void setXHeight(int xHeight) {
+        _xHeight = xHeight;
+    }
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/fonts/FopCharacterSet.java b/src/sandbox/org/apache/fop/render/afp/fonts/FopCharacterSet.java
new file mode 100644 (file)
index 0000000..635cef0
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fonts;
+
+import org.apache.fop.fonts.Typeface;
+
+/**
+ * A Character set for a normal FOP font<p/>
+ */
+public class FopCharacterSet extends CharacterSet {
+
+    /** The character set for this font */
+    private Typeface _characterSet = null;
+    private int _size = 0;
+
+    /**
+     * Constructor for the CharacterSetMetric object, the character set is used
+     * to load the font information from the actual AFP font.
+     * @param codePage the code page identifier
+     * @param encoding the encoding of the font
+     * @param name the character set name
+     * @param size the font size
+     * @param Typeface the fop character set
+     */
+    public FopCharacterSet(
+        String codePage,
+        String encoding,
+        String name,
+        int size,
+        Typeface characterSet) {
+        super(codePage, encoding, name, null);
+        _characterSet = characterSet;
+        _size = size * 1000;
+    }
+
+    /**
+     * Ascender height is the distance from the character baseline to the
+     * top of the character box. A negative ascender height signifies that
+     * all of the graphic character is below the character baseline. For
+     * a character rotation other than 0, ascender height loses its
+     * meaning when the character is lying on its side or is upside down
+     * with respect to normal viewing orientation. For the general case,
+     * Ascender Height is the character�s most positive y-axis value.
+     * For bounded character boxes, for a given character having an
+     * ascender, ascender height and baseline offset are equal.
+     * @return the ascender value in millipoints
+     */
+    public int getAscender() {
+        return _characterSet.getAscender(_size);
+    }
+
+    /**
+     * Cap height is the average height of the uppercase characters in
+     * a font. This value is specified by the designer of a font and is
+     * usually the height of the uppercase M.
+     * @return the cap height value in millipoints
+     */
+    public int getCapHeight() {
+        return _characterSet.getCapHeight(_size);
+    }
+
+    /**
+     * Descender depth is the distance from the character baseline to
+     * the bottom of a character box. A negative descender depth signifies
+     * that all of the graphic character is above the character baseline.
+     * @return the descender value in millipoints
+     */
+    public int getDescender() {
+        return _characterSet.getDescender(_size);
+    }
+
+    /**
+     * The first character in the character set
+     * @return the first character
+     */
+    public int getFirstChar() {
+        return 0;
+    }
+
+    /**
+     * The last character in the character set
+     * @return the last character
+     */
+    public int getLastChar() {
+        return 0;
+    }
+
+    /**
+     * Get the width (in 1/1000ths of a point size) of all characters
+     * @return the widths of all characters
+     */
+    public int[] getWidths() {
+        return _characterSet.getWidths();
+    }
+
+    /**
+     * XHeight refers to the height of the lower case letters above the baseline.
+     * @return the typical height of characters
+     */
+    public int getXHeight() {
+        return _characterSet.getXHeight(_size);
+    }
+
+    /**
+     * Get the width (in 1/1000ths of a point size) of the character
+     * identified by the parameter passed.
+     * @param character the character from which the width will be calculated
+     * @return the width of the character
+     */
+    public int width(int character) {
+        return _characterSet.getWidth(character, _size);
+    }
+
+    /**
+     * Map a Unicode character to a code point in the font.
+     * @param c character to map
+     * @return the mapped character
+     */
+    public char mapChar(char c) {
+        return _characterSet.mapChar(c);
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/fonts/OutlineFont.java b/src/sandbox/org/apache/fop/render/afp/fonts/OutlineFont.java
new file mode 100644 (file)
index 0000000..00757f4
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fonts;
+
+
+/**
+ * A font defined as a set of lines and curves as opposed to a bitmap font. An
+ * outline font can be scaled to any size and otherwise transformed more easily
+ * than a bitmap font, and with more attractive results. <p/>
+ *
+ */
+public class OutlineFont extends AFPFont {
+
+    /** The character set for this font */
+    private CharacterSet _characterSet = null;
+
+    /**
+     * Constructor for an outline font.
+     *
+     * @param name
+     *            the name of the font
+     * @param characterSet
+     *            the chracter set
+     */
+    public OutlineFont(String name, CharacterSet characterSet) {
+        super(name);
+        _characterSet = characterSet;
+    }
+
+    /**
+     * Get the character set metrics.
+     *
+     * @return the character set
+     */
+    public CharacterSet getCharacterSet() {
+
+        return _characterSet;
+
+    }
+
+    /**
+     * Get the character set metrics.
+     * @param size ignored
+     * @return the character set
+     */
+    public CharacterSet getCharacterSet(int size) {
+
+        return _characterSet;
+
+    }
+
+    /**
+     * Get the first character in this font.
+     */
+    public int getFirstChar() {
+
+        return _characterSet.getFirstChar();
+
+    }
+
+    /**
+     * Get the last character in this font.
+     */
+    public int getLastChar() {
+
+        return _characterSet.getLastChar();
+
+    }
+
+    /**
+     * The ascender is the part of a lowercase letter that extends above the
+     * "x-height" (the height of the letter "x"), such as "d", "t", or "h". Also
+     * used to denote the part of the letter extending above the x-height.
+     *
+     * @param size
+     *            the point size
+     */
+    public int getAscender(int size) {
+
+        return _characterSet.getAscender() / 1000 * size;
+
+    }
+
+    /**
+     * Obtains the height of capital letters for the specified point size.
+     *
+     * @param size
+     *            the point size
+     */
+    public int getCapHeight(int size) {
+
+        return _characterSet.getCapHeight() / 1000 * size;
+
+    }
+
+    /**
+     * The descender is the part of a lowercase letter that extends below the
+     * base line, such as "g", "j", or "p". Also used to denote the part of the
+     * letter extending below the base line.
+     *
+     * @param size
+     *            the point size
+     */
+    public int getDescender(int size) {
+
+        return _characterSet.getDescender() / 1000 * size;
+
+    }
+
+    /**
+     * The "x-height" (the height of the letter "x").
+     *
+     * @param size
+     *            the point size
+     */
+    public int getXHeight(int size) {
+
+        return _characterSet.getXHeight() / 1000 * size;
+
+    }
+
+    /**
+     * Obtain the width of the character for the specified point size.
+     */
+    public int getWidth(int character, int size) {
+
+        return _characterSet.width(character) / 1000 * size;
+
+    }
+
+    /**
+     * Get the getWidth (in 1/1000ths of a point size) of all characters in this
+     * character set.
+     *
+     * @param size
+     *            the point size
+     * @return the widths of all characters
+     */
+    public int[] getWidths(int size) {
+
+        int[] widths =  _characterSet.getWidths();
+        for (int i = 0 ; i < widths.length; i++) {
+            widths[i] = widths[i] / 1000 * size;
+        }
+        return widths;
+
+    }
+
+    /**
+     * Get the getWidth (in 1/1000ths of a point size) of all characters in this
+     * character set.
+     *
+     * @return the widths of all characters
+     */
+    public int[] getWidths() {
+
+        return getWidths(1000);
+
+    }
+
+    /**
+     * Map a Unicode character to a code point in the font.
+     * @param c character to map
+     * @return the mapped character
+     */
+    public char mapChar(char c) {
+        return _characterSet.mapChar(c);
+    }
+
+    /**
+     * Get the encoding of the font.
+     * @return the encoding
+     */
+    public String getEncoding() {
+        return _characterSet.getEncoding();
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/fonts/RasterFont.java b/src/sandbox/org/apache/fop/render/afp/fonts/RasterFont.java
new file mode 100644 (file)
index 0000000..cec80c5
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.fonts;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.render.afp.exceptions.FontRuntimeException;
+
+/**
+ * A font where each character is stored as an array of pixels (a bitmap). Such
+ * fonts are not easily scalable, in contrast to vectored fonts. With this type
+ * of font, the font metrics information is held in character set files (one for
+ * each size and style). <p/>
+ *
+ */
+public class RasterFont extends AFPFont {
+
+    /** Static logging instance */
+    protected static final Log log = LogFactory.getLog("org.apache.fop.render.afp.fonts");
+
+    private HashMap _characterSets = new HashMap();
+
+    private CharacterSet _characterSet = null;
+
+    /**
+     * Constructor for the raster font requires the name, weight and style
+     * attribute to be available as this forms the key to the font.
+     *
+     * @param name
+     *            the name of the font
+     */
+    public RasterFont(String name) {
+        super(name);
+    }
+
+    public void addCharacterSet(int size, CharacterSet characterSet) {
+
+        _characterSets.put(String.valueOf(size), characterSet);
+
+        _characterSet = characterSet;
+
+    }
+
+    /**
+     * Get the character set metrics for the specified point size.
+     *
+     * @param size the point size
+     * @return the character set metrics
+     */
+    public CharacterSet getCharacterSet(int size) {
+
+        String pointsize = String.valueOf(size / 1000);
+        CharacterSet csm = (CharacterSet) _characterSets.get(pointsize);
+        if (csm == null) {
+            csm = (CharacterSet) _characterSets.get(size + "mpt");
+        }
+        if (csm == null) {
+            // Get char set with nearest font size
+            int distance = Integer.MAX_VALUE;
+            for (Iterator it = _characterSets.entrySet().iterator(); it.hasNext(); ) {
+                Map.Entry me = (Map.Entry)it.next();
+                String key = (String)me.getKey();
+                if (!key.endsWith("mpt")) {
+                    int mpt = Integer.parseInt(key) * 1000;
+                    if (Math.abs(size - mpt) < distance) {
+                        distance = Math.abs(size - mpt);
+                        pointsize = (String)me.getKey();
+                        csm = (CharacterSet)me.getValue();
+                    }
+                }
+            }
+            if (csm != null) {
+                _characterSets.put(size + "mpt", csm);
+                String msg = "No " + (size / 1000) + "pt font " + _name
+                    + " found, substituted with " + pointsize + "pt font";
+                log.warn(msg);
+            }
+        }
+        if (csm == null) {
+            String msg = "No font found for font " + _name
+                + " with point size " + pointsize;
+            log.error(msg);
+            throw new FontRuntimeException(msg);
+        }
+        return csm;
+
+    }
+
+    /**
+     * Get the first character in this font.
+     */
+    public int getFirstChar() {
+
+        Iterator i = _characterSets.values().iterator();
+        if (i.hasNext()) {
+            CharacterSet csm = (CharacterSet) i.next();
+            return csm.getFirstChar();
+        } else {
+            String msg = "getFirstChar() - No character set found for font:" + _name;
+            log.error(msg);
+            throw new FontRuntimeException(msg);
+        }
+
+    }
+
+    /**
+     * Get the last character in this font.
+     */
+    public int getLastChar() {
+
+        Iterator i = _characterSets.values().iterator();
+        if (i.hasNext()) {
+            CharacterSet csm = (CharacterSet) i.next();
+            return csm.getLastChar();
+        } else {
+            String msg = "getLastChar() - No character set found for font:" + _name;
+            log.error(msg);
+            throw new FontRuntimeException(msg);
+        }
+
+    }
+
+    /**
+     * The ascender is the part of a lowercase letter that extends above the
+     * "x-height" (the height of the letter "x"), such as "d", "t", or "h". Also
+     * used to denote the part of the letter extending above the x-height.
+     *
+     * @param size the point size
+     */
+    public int getAscender(int size) {
+
+        return getCharacterSet(size).getAscender();
+
+    }
+
+    /**
+     * Obtains the height of capital letters for the specified point size.
+     *
+     * @param size the point size
+     */
+    public int getCapHeight(int size) {
+
+        return getCharacterSet(size).getCapHeight();
+
+    }
+
+    /**
+     * The descender is the part of a lowercase letter that extends below the
+     * base line, such as "g", "j", or "p". Also used to denote the part of the
+     * letter extending below the base line.
+     *
+     * @param size the point size
+     */
+    public int getDescender(int size) {
+
+        return getCharacterSet(size).getDescender();
+
+    }
+
+    /**
+     * The "x-height" (the height of the letter "x").
+     *
+     * @param size the point size
+     */
+    public int getXHeight(int size) {
+
+        return getCharacterSet(size).getXHeight();
+
+    }
+
+    /**
+     * Obtain the width of the character for the specified point size.
+     */
+    public int getWidth(int character, int size) {
+
+        return getCharacterSet(size).width(character);
+
+    }
+
+    /**
+     * Get the getWidth (in 1/1000ths of a point size) of all characters in this
+     * character set.
+     *
+     * @param size
+     *            the point size
+     * @return the widths of all characters
+     */
+    public int[] getWidths(int size) {
+
+        return getCharacterSet(size).getWidths();
+
+    }
+
+    /**
+     * Get the getWidth (in 1/1000ths of a point size) of all characters in this
+     * character set.
+     *
+     * @return the widths of all characters
+     */
+    public int[] getWidths() {
+
+        return getWidths(1000);
+
+    }
+
+    /**
+     * Map a Unicode character to a code point in the font.
+     * @param c character to map
+     * @return the mapped character
+     */
+    public char mapChar(char c) {
+        return _characterSet.mapChar(c);
+    }
+
+    /**
+     * Get the encoding of the font.
+     * @return the encoding
+     */
+    public String getEncoding() {
+        return _characterSet.getEncoding();
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/AFPConstants.java b/src/sandbox/org/apache/fop/render/afp/modca/AFPConstants.java
new file mode 100644 (file)
index 0000000..9c23e3f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+/**
+ * Constants used by the AFP renderer.
+ *
+ */
+public interface AFPConstants {
+
+    /**
+     * The encoding to use to convert to EBCIDIC
+     */
+    public String EBCIDIC_ENCODING = "Cp1146";
+
+    /**
+     * The encoding to use to convert to ASCII
+     */
+    public String ASCII_ENCODING = "Cp1252";
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/AFPDataStream.java b/src/sandbox/org/apache/fop/render/afp/modca/AFPDataStream.java
new file mode 100644 (file)
index 0000000..32fc298
--- /dev/null
@@ -0,0 +1,650 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.render.afp.AFPFontColor;
+import org.apache.fop.render.afp.fonts.AFPFont;
+import org.apache.fop.render.afp.tools.StringUtils;
+
+/**
+ * A data stream is a continuous ordered stream of data elements and objects
+ * conforming to a given format. Application programs can generate data streams
+ * destined for a presentation service, archive library, presentation device or
+ * another application program. The strategic presentation data stream
+ * architectures used is Mixed Object Document Content Architecture (MO:DCA�).
+ *
+ * The MO:DCA architecture defines the data stream used by applications to
+ * describe documents and object envelopes for interchange with other
+ * applications and application services. Documents defined in the MO:DCA format
+ * may be archived in a database, then later retrieved, viewed, annotated and
+ * printed in local or distributed systems environments. Presentation fidelity
+ * is accommodated by including resource objects in the documents that reference
+ * them.
+ *
+ */
+public class AFPDataStream {
+
+    /**
+     * Static logging instance
+     */
+    protected static final Log log = LogFactory.getLog("org.apache.fop.render.afp.modca");
+
+    /**
+     * Boolean completion indicator
+     */
+    private boolean _complete = false;
+
+    /**
+     * The application producing the AFP document
+     */
+    private String _producer = null;
+
+    /**
+     * The AFP document object
+     */
+    private Document _document = null;
+
+    /**
+     * The current page group object
+     */
+    private PageGroup _currentPageGroup = null;
+
+    /**
+     * The current page object
+     */
+    private PageObject _currentPageObject = null;
+
+    /**
+     * The current overlay object
+     */
+    private Overlay _currentOverlay = null;
+
+    /**
+     * The current page
+     */
+    private AbstractPageObject _currentPage = null;
+
+    /**
+     * The page count
+     */
+    private int _pageCount = 0;
+
+    /**
+     * The page group count
+     */
+    private int _pageGroupCount = 0;
+
+    /**
+     * The overlay count
+     */
+    private int _ovlCount = 0;
+
+    /**
+     * The portrait rotation
+     */
+    private int _portraitRotation = 0;
+
+    /**
+     * The landscape rotation
+     */
+    private int _landscapeRotation = 270;
+
+    /**
+     * The x offset
+     */
+    private int _xOffset = 0;
+
+    /**
+     * The y offset
+     */
+    private int _yOffset = 0;
+
+    /**
+     * The rotation
+     */
+    private int _rotation;
+
+    /**
+     * The outputstream for the data stream
+     */
+    private OutputStream _outputStream = null;
+
+    /**
+     * Default constructor for the AFPDataStream.
+     */
+    public AFPDataStream() {
+    }
+
+    /**
+     * The document is started by invoking this method which creates an instance
+     * of the AFP Document object.
+     */
+    public void startDocument(OutputStream outputStream) {
+
+        if (_document != null) {
+            String msg = "Invalid state - document already started.";
+            log.warn("startDocument():: " + msg);
+            throw new IllegalStateException(msg);
+        }
+
+        _document = new Document();
+        _outputStream = outputStream;
+
+    }
+
+    /**
+     * The document is ended by invoking this method which creates an instance
+     * of the AFP Document object and registers the start with a validation map
+     * which ensures that methods are not invoked out of the correct sequence.
+     */
+    public void endDocument()
+        throws IOException {
+
+        if (_complete) {
+            String msg = "Invalid state - document already ended.";
+            log.warn("endDocument():: " + msg);
+            throw new IllegalStateException(msg);
+        }
+
+        if (_currentPageObject != null) {
+            // End the current page if necessary
+            endPage();
+        }
+
+        if (_currentPageGroup != null) {
+            // End the current page group if necessary
+            endPageGroup();
+        }
+
+        _document.endDocument();
+        _document.writeDataStream(_outputStream);
+        _outputStream.flush();
+
+        _complete = true;
+
+        _document = null;
+
+        _outputStream = null;
+    }
+
+    /**
+     * Start a new page. When processing has finished on the current page, the
+     * {@link #endPage()}method must be invoked to mark the page ending.
+     *
+     * @param pageWidth
+     *            the width of the page
+     * @param pageHeight
+     *            the height of the page
+     * @param pageRotation
+     *            the rotation of the page
+     */
+    public void startPage(int pageWidth, int pageHeight, int pageRotation) {
+
+        String pageName = "PGN"
+            + StringUtils.lpad(String.valueOf(_pageCount++), '0', 5);
+
+        _currentPageObject = new PageObject(pageName, pageWidth, pageHeight, pageRotation);
+        _currentPage = _currentPageObject;
+        _currentOverlay = null;
+        setOffsets(0, 0, 0);
+    }
+
+    /**
+     * Start a new overlay. When processing has finished on the current overlay, the
+     * {@link #endOverlay()}method must be invoked to mark the overlay ending.
+     *
+     * @param overlayX
+     *            the x position of the overlay on the page
+     * @param overlayY
+     *            the y position of the overlay on the page
+     * @param overlayWidth
+     *            the width of the overlay
+     * @param overlayHeight
+     *            the height of the overlay
+     * @param overlayRotation
+     *            the rotation of the overlay
+     */
+    public void startOverlay(int overlayX, int overlayY, int overlayWidth, int overlayHeight, int overlayRotation) {
+
+        String overlayName = "OVL"
+            + StringUtils.lpad(String.valueOf(_ovlCount++), '0', 5);
+
+        _currentOverlay = new Overlay(overlayName, overlayWidth, overlayHeight, overlayRotation);
+        _currentPageObject.addOverlay(_currentOverlay);
+        _currentPageObject.createIncludePageOverlay(overlayName, overlayX, overlayY, 0);
+        _currentPage = _currentOverlay;
+        setOffsets(0, 0, 0);
+    }
+
+    /**
+     * Helper method to mark the end of the current overlay.
+     */
+    public void endOverlay() {
+
+        _currentOverlay.endPage();
+        _currentOverlay = null;
+        _currentPage = _currentPageObject;
+
+    }
+
+    /**
+     * Helper method to save the current page.
+     */
+    public PageObject savePage() {
+
+        PageObject pageObject = _currentPageObject;
+        if (_currentPageGroup != null) {
+            _currentPageGroup.addPage(_currentPageObject);
+        } else {
+            _document.addPage(_currentPageObject);
+        }
+        _currentPageObject = null;
+        _currentPage = null;
+        return pageObject;
+
+    }
+
+    /**
+     * Helper method to restore the current page.
+     */
+    public void restorePage(PageObject pageObject) {
+
+        _currentPageObject = pageObject;
+        _currentPage = pageObject;
+
+    }
+
+    /**
+     * Helper method to mark the end of the current page.
+     */
+    public void endPage()
+        throws IOException {
+
+        _currentPageObject.endPage();
+        if (_currentPageGroup != null) {
+            _currentPageGroup.addPage(_currentPageObject);
+        } else {
+            _document.addPage(_currentPageObject);
+            _document.writeDataStream(_outputStream);
+        }
+
+        _currentPageObject = null;
+        _currentPage = null;
+
+    }
+
+    /**
+     * Sets the offsets to be used for element positioning
+     *
+     * @param xOffset
+     *            the offset in the x direction
+     * @param yOffset
+     *            the offset in the y direction
+     * @param rotation
+     *            the rotation
+     */
+    public void setOffsets(int xOffset, int yOffset, int rotation) {
+        _xOffset = xOffset;
+        _yOffset = yOffset;
+        _rotation = rotation;
+    }
+
+    /**
+     * Helper method to create a map coded font object on the current page, this
+     * method delegates the construction of the map coded font object to the
+     * active environment group on the current page.
+     *
+     * @param fontReference
+     *            the font number used as the resource identifier
+     * @param font
+     *            the font
+     * @param size
+     *            the point size of the font
+     */
+    public void createFont(byte fontReference, AFPFont font, int size) {
+
+        _currentPage.createFont(fontReference, font, size);
+
+    }
+
+    /**
+     * Helper method to create text on the current page, this method delegates
+     * to the current presentation text object in order to construct the text.
+     *
+     * @param fontNumber
+     *            the font number used as the resource identifier
+     * @param x
+     *            the x coordinate of the text
+     * @param y
+     *            the y coordinate of the text
+     * @param col
+     *            the text color
+     * @param vsci
+     *            The variable space character increment.
+     * @param ica
+     *            The inter character adjustment.
+     * @param data
+     *            the text data to create
+     */
+    public void createText(int fontNumber, int x, int y, AFPFontColor col, int vsci, int ica, byte[] data) {
+
+        _currentPage.createText(fontNumber, x + _xOffset, y + _yOffset, _rotation, col, vsci, ica, data);
+
+    }
+
+    /**
+     * Returns an ImageObject used to create an image in the datastream.
+     *
+     * @param x
+     *            the x position of the image
+     * @param y
+     *            the y position of the image
+     * @param w
+     *            the width of the image
+     * @param h
+     *            the height of the image
+     */
+    public ImageObject getImageObject(int x, int y, int w, int h) {
+
+        int xOrigin;
+        int yOrigin;
+        int width;
+        int height;
+        switch (_rotation) {
+            case 90:
+                xOrigin = _currentPage.getWidth() - y - _yOffset;
+                yOrigin = x + _xOffset;
+                width = h;
+                height = w;
+                break;
+            case 180:
+                xOrigin = _currentPage.getWidth() - x - _xOffset;
+                yOrigin = _currentPage.getHeight() - y - _yOffset;
+                width = w;
+                height = h;
+                break;
+            case 270:
+                xOrigin = y + _yOffset;
+                yOrigin = _currentPage.getHeight() - x - _xOffset;
+                width = h;
+                height = w;
+                break;
+            default:
+                xOrigin = x + _xOffset;
+                yOrigin = y + _yOffset;
+                width = w;
+                height = h;
+                break;
+        }
+        ImageObject io = _currentPage.getImageObject();
+        io.setImageViewport(xOrigin, yOrigin, width, height, _rotation);
+        return io;
+
+    }
+
+    /**
+     * Method to create a line on the current page.
+     *
+     * @param x1
+     *            the first x coordinate of the line
+     * @param y1
+     *            the first y coordinate of the line
+     * @param x2
+     *            the second x coordinate of the line
+     * @param y2
+     *            the second y coordinate of the line
+     * @param thickness
+     *            the thickness of the line
+     * @param col
+     *            The text color.
+     */
+    public void createLine(int x1, int y1, int x2, int y2, int thickness, AFPFontColor col) {
+
+        _currentPage.createLine(x1 + _xOffset, y1 + _yOffset, x2 + _xOffset, y2 + _yOffset, thickness, _rotation, col);
+
+    }
+
+    /**
+     * Sets the application producing the AFP.
+     *
+     * @param producer
+     *            the application producing the AFP datastream
+     */
+    public void setProducer(String producer) {
+        _producer = producer;
+    }
+
+    /**
+     * This method will create shading on the page using the specified
+     * coordinates (the shading contrast is controlled via the red, green, blue
+     * parameters, by converting this to grey scale).
+     *
+     * @param x
+     *            the x coordinate of the shading
+     * @param y
+     *            the y coordinate of the shading
+     * @param w
+     *            the width of the shaded area
+     * @param h
+     *            the height of the shaded area
+     * @param red
+     *            the red value
+     * @param green
+     *            the green value
+     * @param blue
+     *            the blue value
+     */
+    public void createShading(int x, int y, int w, int h, int red, int green,
+        int blue) {
+
+        _currentPage.createShading(x + _xOffset, y + _xOffset, w, h, red, green, blue);
+
+    }
+
+    /**
+     * Helper method which allows creation of the MPO object, via the AEG. And
+     * the IPO via the Page. (See actual object for descriptions.)
+     *
+     * @param name
+     *            the name of the static overlay
+     */
+    public void createIncludePageOverlay(String name) {
+
+        _currentPageObject.createIncludePageOverlay(name, 0, 0, _rotation);
+        ActiveEnvironmentGroup aeg = _currentPageObject.getActiveEnvironmentGroup();
+        aeg.createOverlay(name);
+
+    }
+
+    /**
+     * Helper method which allows creation of the IMM object.
+     *
+     * @param name
+     *            the name of the medium map
+     */
+    public void createInvokeMediumMap(String name) {
+
+        if (_currentPageGroup == null) {
+            startPageGroup();
+        }
+        _currentPageGroup.createInvokeMediumMap(name);
+
+    }
+
+    /**
+     * Creates an IncludePageSegment on the current page.
+     *
+     * @param name
+     *            the name of the include page segment
+     * @param x
+     *            the x coordinate for the overlay
+     * @param y
+     *            the y coordinate for the overlay
+     */
+    public void createIncludePageSegment(String name, int x, int y) {
+
+        int xOrigin;
+        int yOrigin;
+        switch (_rotation) {
+            case 90:
+                xOrigin = _currentPage.getWidth() - y - _yOffset;
+                yOrigin = x + _xOffset;
+                break;
+            case 180:
+                xOrigin = _currentPage.getWidth() - x - _xOffset;
+                yOrigin = _currentPage.getHeight() - y - _yOffset;
+                break;
+            case 270:
+                xOrigin = y + _yOffset;
+                yOrigin = _currentPage.getHeight() - x - _xOffset;
+                break;
+            default:
+                xOrigin = x + _xOffset;
+                yOrigin = y + _yOffset;
+                break;
+        }
+        _currentPage.createIncludePageSegment(name, xOrigin, yOrigin);
+
+    }
+
+    /**
+     * Creates a TagLogicalElement on the current page.
+     *
+     * @param attributes
+     *            the array of key value pairs.
+     */
+    public void createPageTagLogicalElement(TagLogicalElementBean[] attributes) {
+
+        for (int i = 0; i < attributes.length; i++) {
+            String name = (String) attributes[i].getKey();
+            String value = (String) attributes[i].getValue();
+            _currentPage.createTagLogicalElement(name, value);
+        }
+
+    }
+
+    /**
+     * Creates a TagLogicalElement on the current page group.
+     *
+     * @param attributes
+     *            the array of key value pairs.
+     */
+    public void createPageGroupTagLogicalElement(
+        TagLogicalElementBean[] attributes) {
+
+        for (int i = 0; i < attributes.length; i++) {
+            String name = (String) attributes[i].getKey();
+            String value = (String) attributes[i].getValue();
+            _currentPageGroup.createTagLogicalElement(name, value);
+        }
+
+    }
+
+    /**
+     * Creates a TagLogicalElement on the current page or page group
+     *
+     * @param name
+     *            The tag name
+     * @param value
+     *            The tag value
+     */
+    public void createTagLogicalElement(String name, String value) {
+
+        if (_currentPageGroup != null) {
+            _currentPageGroup.createTagLogicalElement(name, value);
+        } else {
+            _currentPage.createTagLogicalElement(name, value);
+        }
+
+    }
+
+    /**
+     * Start a new page group. When processing has finished on the current page
+     * group the {@link #endPageGroup()}method must be invoked to mark the page
+     * group ending.
+     *
+     * @param name
+     *            the name of the page group
+     */
+    public void startPageGroup() {
+
+        String pageGroupName = "PGP"
+            + StringUtils.lpad(String.valueOf(_pageCount++), '0', 5);
+
+        _currentPageGroup = new PageGroup(pageGroupName);
+
+    }
+
+    /**
+     * Helper method to mark the end of the page group.
+     */
+    public void endPageGroup()
+        throws IOException {
+
+        _currentPageGroup.endPageGroup();
+        _document.addPageGroup(_currentPageGroup);
+        _document.writeDataStream(_outputStream);
+        _currentPageGroup = null;
+
+    }
+
+    /**
+     * Sets the rotation to be used for portrait pages, valid values are 0
+     * (default), 90, 180, 270.
+     *
+     * @param rotation
+     *            The rotation in degrees.
+     */
+    public void setPortraitRotation(int rotation) {
+
+        if (rotation == 0 || rotation == 90 || rotation == 180
+            || rotation == 270) {
+            _portraitRotation = rotation;
+        } else {
+            throw new IllegalArgumentException(
+                "The portrait rotation must be one of the values 0, 90, 180, 270");
+        }
+
+    }
+
+    /**
+     * Sets the rotation to be used for landscape pages, valid values are 0, 90,
+     * 180, 270 (default).
+     *
+     * @param rotation
+     *            The rotation in degrees.
+     */
+    public void setLandscapeRotation(int rotation) {
+
+        if (rotation == 0 || rotation == 90 || rotation == 180
+            || rotation == 270) {
+            _landscapeRotation = rotation;
+        } else {
+            throw new IllegalArgumentException(
+                "The landscape rotation must be one of the values 0, 90, 180, 270");
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/AbstractAFPObject.java b/src/sandbox/org/apache/fop/render/afp/modca/AbstractAFPObject.java
new file mode 100644 (file)
index 0000000..c7905ea
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This is the base class for all data stream objects. Page objects are
+ * responsible for building and generating the binary datastream in an
+ * AFP format.
+ *
+ */
+public abstract class AbstractAFPObject {
+
+    /**
+     * Static logging instance
+     */
+    protected static final Log log = LogFactory.getLog("org.apache.fop.render.afp.modca");
+
+    /**
+     * DataStream objects must implement the writeDataStream()
+     * method to write its data to the given OutputStream
+     * @param os The outputsteam stream
+     * @throws java.io.IOException
+     */
+    public abstract void writeDataStream(OutputStream os) throws IOException;
+
+    /**
+     * Help method to write a set of AFPObjects to the AFP datastream.
+     * @afpObjects a list of AFPObjects
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    protected void writeObjectList(List afpObjects, OutputStream os)
+        throws IOException {
+
+        for (Iterator it = afpObjects.iterator(); it.hasNext(); ) {
+            ((AbstractAFPObject)it.next()).writeDataStream(os);
+        }
+
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/AbstractPageObject.java b/src/sandbox/org/apache/fop/render/afp/modca/AbstractPageObject.java
new file mode 100644 (file)
index 0000000..5056e9b
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.fop.render.afp.AFPFontColor;
+import org.apache.fop.render.afp.fonts.AFPFont;
+import org.apache.fop.render.afp.tools.StringUtils;
+
+/**
+ * Pages contain the data objects that comprise a presentation document. Each
+ * page has a set of data objects associated with it. Each page within a
+ * document is independent from any other page, and each must establish its own
+ * environment parameters.
+ *
+ * The page is the level in the document component hierarchy that is used for
+ * printing or displaying a document's content. The data objects contained in
+ * the page envelope in the data stream are presented when the page is
+ * presented. Each data object has layout information associated with it that
+ * directs the placement and orientation of the data on the page. In addition,
+ * each page contains layout information that specifies the measurement units,
+ * page width, and page depth.
+ *
+ * A page is initiated by a begin page structured field and terminated by an end
+ * page structured field. Structured fields that define objects and active
+ * environment groups or that specify attributes of the page may be encountered
+ * in page state.
+ *
+ */
+public abstract class AbstractPageObject extends AbstractNamedAFPObject {
+
+    /**
+     * The active environment group for the page
+     */
+    protected ActiveEnvironmentGroup _activeEnvironmentGroup = null;
+
+    /**
+     * The presentation text object, we only have one per page
+     */
+    private PresentationTextObject _presentationTextObject = null;
+
+    /**
+     * The list of objects within the page
+     */
+    protected List _objects = new ArrayList();
+
+    /**
+     * The list of tag logical elements
+     */
+    protected ArrayList _tagLogicalElements = new ArrayList();
+
+    /**
+     * The list of the include page segments
+     */
+    protected ArrayList _segments = new ArrayList();
+
+    /**
+     * The page width
+     */
+    private int _width;
+
+    /**
+     * The page height
+     */
+    private int _height;
+
+    /**
+     * The page rotation
+     */
+    private int _rotation = 0;
+
+    /**
+     * The page state
+     */
+    private boolean _complete = false;
+
+    /**
+     * Construct a new page object for the specified name argument, the page
+     * name should be an 8 character identifier.
+     *
+     * @param name
+     *            the name of the page.
+     * @param width
+     *            the width of the page.
+     * @param height
+     *            the height of the page.
+     * @param rotation
+     *            the rotation of the page.
+     */
+    public AbstractPageObject(String name, int width, int height, int rotation) {
+
+        super(name);
+
+        _name = name;
+
+        _rotation = rotation;
+        _width = width;
+        _height = height;
+
+        /**
+         * Every page object must have an ActiveEnvironmentGroup
+         */
+        _activeEnvironmentGroup = new ActiveEnvironmentGroup(_width, _height);
+
+        if (_rotation != 0) {
+            switch (_rotation) {
+                case 90:
+                    _activeEnvironmentGroup.setPosition(_width, 0, _rotation);
+                    break;
+                case 180:
+                    _activeEnvironmentGroup.setPosition(_width, _height, _rotation);
+                    break;
+                case 270:
+                    _activeEnvironmentGroup.setPosition(0, _height, _rotation);
+                    break;
+            }
+        }
+
+        /**
+         * We have a presentation text object per page
+         */
+        _presentationTextObject = new PresentationTextObject();
+        _objects.add(_presentationTextObject);
+
+    }
+
+    /**
+     * Helper method to create a map coded font object on the current page, this
+     * method delegates the construction of the map coded font object to the
+     * active environment group on the page.
+     *
+     * @param fontReference
+     *            the font number used as the resource identifier
+     * @param font
+     *            the font
+     * @param size
+     *            the point size of the font
+     */
+    public void createFont(byte fontReference, AFPFont font, int size) {
+
+        _activeEnvironmentGroup.createFont(fontReference, font, size, 0);
+
+    }
+
+    /**
+     * Helper method to create a line on the current page, this method delegates
+     * to the presentation text object in order to construct the line.
+     *
+     * @param x1
+     *            the first x coordinate of the line
+     * @param y1
+     *            the first y coordinate of the line
+     * @param x2
+     *            the second x coordinate of the line
+     * @param y2
+     *            the second y coordinate of the line
+     * @param thickness
+     *            the thickness of the line
+     * @param rotation
+     *            the rotation of the line
+     * @param col
+     *            The text color.
+     */
+    public void createLine(int x1, int y1, int x2, int y2, int thickness, int rotation, AFPFontColor col) {
+
+        if (_presentationTextObject == null) {
+            _presentationTextObject = new PresentationTextObject();
+            _objects.add(_presentationTextObject);
+        }
+        _presentationTextObject.createLineData(x1, y1, x2, y2, thickness, rotation, col);
+
+    }
+
+    /**
+     * Helper method to create text on the current page, this method delegates
+     * to the presentation text object in order to construct the text.
+     *
+     * @param fontNumber
+     *            the font number used as the resource identifier
+     * @param x
+     *            the x coordinate of the text data
+     * @param y
+     *            the y coordinate of the text data
+     * @param rotation
+     *            the rotation of the text data
+     * @param col
+     *            the text color
+     * @param vsci
+     *            The variable space character increment.
+     * @param ica
+     *            The inter character adjustment.
+     * @param data
+     *            the text data to create
+     */
+    public void createText(int fontNumber, int x, int y, int rotation, AFPFontColor col, int vsci, int ica, byte[] data) {
+
+        if (_presentationTextObject == null) {
+            _presentationTextObject = new PresentationTextObject();
+            _objects.add(_presentationTextObject);
+        }
+        _presentationTextObject.createTextData(fontNumber, x, y, rotation, col, vsci, ica, data);
+
+    }
+
+    /**
+     * Helper method to mark the end of the page. This should end the control
+     * sequence on the current presenation text object.
+     */
+    public void endPage() {
+
+        _presentationTextObject.endControlSequence();
+
+        _complete = true;
+
+    }
+
+    /**
+     * This method will create shading on the page using the specified
+     * coordinates (the shading contrast is controlled via the red, green blue
+     * parameters, by converting this to grey scale).
+     *
+     * @param x
+     *            the x coordinate of the shading
+     * @param y
+     *            the y coordinate of the shading
+     * @param w
+     *            the width of the shaded area
+     * @param h
+     *            the height of the shaded area
+     * @param red
+     *            the red value
+     * @param green
+     *            the green value
+     * @param blue
+     *            the blue value
+     */
+    public void createShading(int x, int y, int w, int h, int red, int green,
+        int blue) {
+
+        int xCoord = 0;
+        int yCoord = 0;
+        int width = 0;
+        int height = 0;
+
+        switch (_rotation) {
+            case 90:
+                xCoord = _width - y - h;
+                yCoord = x;
+                width = h;
+                height = w;
+                break;
+            case 180:
+                xCoord = _width - x - w;
+                yCoord = _height - y - h;
+                width = w;
+                height = h;
+                break;
+            case 270:
+                xCoord = y;
+                yCoord = _height - x - w;
+                width = h;
+                height = w;
+                break;
+            default:
+                xCoord = x;
+                yCoord = y;
+                width = w;
+                height = h;
+                break;
+        }
+
+        // Convert the color to grey scale
+        float shade = (float) ((red * 0.3) + (green * 0.59) + (blue * 0.11));
+
+        int greyscale = Math.round((shade / 255) * 16);
+
+        String imageName = "IMG"
+            + StringUtils.lpad(String.valueOf(_objects.size() + 1),
+            '0', 5);
+
+        IMImageObject io = new IMImageObject(imageName);
+        ImageOutputControl ioc = new ImageOutputControl(0, 0);
+        ImageInputDescriptor iid = new ImageInputDescriptor();
+        ImageCellPosition icp = new ImageCellPosition(xCoord, yCoord);
+        icp.setXFillSize(width);
+        icp.setYFillSize(height);
+        icp.setXSize(64);
+        icp.setYSize(8);
+
+        //defing this as a resource
+        ImageRasterData ird = new ImageRasterData(ImageRasterPattern
+            .getRasterData(greyscale));
+
+        io.setImageOutputControl(ioc);
+        io.setImageInputDescriptor(iid);
+        io.setImageCellPosition(icp);
+        io.setImageRasterData(ird);
+        _objects.add(io);
+
+    }
+
+    /**
+     * Helper method to create an image on the current page and to return
+     * the object.
+     */
+    public ImageObject getImageObject() {
+
+        if (_presentationTextObject != null) {
+            _presentationTextObject.endControlSequence();
+        }
+        _presentationTextObject = null;
+
+        String imageName = "IMG"
+            + StringUtils.lpad(String.valueOf(_objects.size() + 1),
+            '0', 5);
+
+        ImageObject io = new ImageObject(imageName);
+        _objects.add(io);
+        return io;
+    }
+
+    /**
+     * Creates a TagLogicalElement on the page.
+     *
+     * @param name
+     *            the name of the tag
+     * @param value
+     *            the value of the tag
+     */
+    public void createTagLogicalElement(String name, String value) {
+
+        TagLogicalElement tle = new TagLogicalElement(name, value);
+        _tagLogicalElements.add(tle);
+
+    }
+
+    /**
+     * Creates an IncludePageSegment on the current page.
+     *
+     * @param name
+     *            the name of the page segment
+     * @param xCoor
+     *            the x cooridinate of the page segment.
+     * @param yCoor
+     *            the y cooridinate of the page segment.
+     */
+    public void createIncludePageSegment(String name, int xCoor, int yCoor) {
+
+        IncludePageSegment ips = new IncludePageSegment(name, xCoor, yCoor);
+        _segments.add(ips);
+
+    }
+
+    /**
+     * Returns the ActiveEnvironmentGroup associated with this page.
+     *
+     * @return the ActiveEnvironmentGroup object
+     */
+    public ActiveEnvironmentGroup getActiveEnvironmentGroup() {
+        return _activeEnvironmentGroup;
+    }
+
+    /**
+     * Returns an indication if the page is complete
+     */
+    public boolean isComplete() {
+        return _complete;
+    }
+
+    /**
+     * Returns the height of the page
+     */
+    public int getHeight() {
+        return _height;
+    }
+
+    /**
+     * Returns the width of the page
+     */
+    public int getWidth() {
+        return _width;
+    }
+
+    /**
+     * Returns the rotation of the page
+     */
+    public int getRotation() {
+        return _rotation;
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ActiveEnvironmentGroup.java b/src/sandbox/org/apache/fop/render/afp/modca/ActiveEnvironmentGroup.java
new file mode 100644 (file)
index 0000000..3fb7ac2
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import org.apache.fop.render.afp.fonts.AFPFont;
+
+/**
+ * An Active Environment Group (AEG) is associated with each page,
+ * and is contained in the page's begin-end envelope in the data stream.
+ * The active environment group contains layout and formatting information
+ * that defines the measurement units and size of the page, and may contain
+ * resource information.
+ *
+ * Any objects that are required for page presentation and that are to be
+ * treated as resource objects must be mapped with a map structured field
+ * in the AEG. The scope of an active environment group is the scope of its
+ * containing page or overlay.
+ *
+ */
+public final class ActiveEnvironmentGroup extends AbstractNamedAFPObject {
+
+    /**
+     * Default name for the active environment group
+     */
+    private static final String DEFAULT_NAME = "AEG00001";
+
+    /**
+     * The collection of MapCodedFont objects
+     */
+    private ArrayList _mapCodedFonts = new ArrayList();
+
+    /**
+     * The Object Area Descriptor for the active environment group
+     */
+    private ObjectAreaDescriptor _objectAreaDescriptor = null;
+
+    /**
+     * The Object Area Position for the active environment group
+     */
+    private ObjectAreaPosition _objectAreaPosition = null;
+
+    /**
+     * The PresentationTextDescriptor for the active environment group
+     */
+    private PresentationTextDescriptor _presentationTextDataDescriptor = null;
+
+    /**
+     * The PageDescriptor for the active environment group
+     */
+    private PageDescriptor _pageDescriptor = null;
+
+    /**
+     * The collection of MapPageOverlay objects
+     */
+    private ArrayList _mapPageOverlays = new ArrayList();
+
+    /**
+     * Default constructor for the ActiveEnvironmentGroup.
+     * @param width the page width
+     * @param height the page height
+     */
+    public ActiveEnvironmentGroup(int width, int height) {
+
+        this(DEFAULT_NAME, width, height);
+
+    }
+
+    /**
+     * Constructor for the ActiveEnvironmentGroup, this takes a
+     * name parameter which must be 8 characters long.
+     * @param name the active environment group name
+     * @param width the page width
+     * @param height the page height
+     */
+    public ActiveEnvironmentGroup(String name, int width, int height) {
+
+        super(name);
+
+        // Create PageDescriptor
+        _pageDescriptor = new PageDescriptor(width, height);
+
+        // Create ObjectAreaDescriptor
+        _objectAreaDescriptor = new ObjectAreaDescriptor(width, height);
+
+        // Create PresentationTextDataDescriptor
+        _presentationTextDataDescriptor =
+            new PresentationTextDescriptor(width, height);
+
+    }
+
+    /**
+     * Set the position of the object area
+     * @param x the x offset
+     * @param y the y offset
+     * @param rotation the rotation
+     */
+    public void setPosition(int x, int y, int rotation) {
+
+        // Create ObjectAreaPosition
+        _objectAreaPosition = new ObjectAreaPosition(x, y, rotation);
+
+    }
+
+    /**
+     * Accessor method to obtain the PageDescriptor object of the
+     * active environment group.
+     * @return the page descriptor object
+     */
+    public PageDescriptor getPageDescriptor() {
+
+        return _pageDescriptor;
+
+    }
+
+    /**
+     * Accessor method to obtain the PresentationTextDataDescriptor object of
+     * the active environment group.
+     * @return the presentation text descriptor
+     */
+    public PresentationTextDescriptor getPresentationTextDataDescriptor() {
+
+        return _presentationTextDataDescriptor;
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the active environment group.
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        writeStart(os);
+
+        writeObjectList(_mapCodedFonts, os);
+
+        writeObjectList(_mapPageOverlays, os);
+
+        _pageDescriptor.writeDataStream(os);
+
+        if (_objectAreaDescriptor != null && _objectAreaPosition != null) {
+            _objectAreaDescriptor.writeDataStream(os);
+            _objectAreaPosition.writeDataStream(os);
+        }
+
+        _presentationTextDataDescriptor.writeDataStream(os);
+
+        writeEnd(os);
+
+    }
+
+    /**
+     * Helper method to write the start of the active environment group.
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA8; // Structured field id byte 2
+        data[5] = (byte) 0xC9; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+       os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the active environment group.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA9; // Structured field id byte 2
+        data[5] = (byte) 0xC9; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * Method to create a map coded font object
+     * @param fontReference the font number used as the resource identifier
+     * @param font the font
+     * @param size the point size of the font
+     * @param orientation the orientation of the font (e.g. 0, 90, 180, 270)
+     */
+    public void createFont(
+        byte fontReference,
+        AFPFont font,
+        int size,
+        int orientation) {
+
+        MapCodedFont mcf = getCurrentMapCodedFont();
+
+        if (mcf == null) {
+            mcf = new MapCodedFont();
+            _mapCodedFonts.add(mcf);
+        }
+
+        try {
+
+            mcf.addFont(
+                fontReference,
+                font,
+                size,
+                orientation);
+
+        } catch (MaximumSizeExceededException msee) {
+
+            mcf = new MapCodedFont();
+            _mapCodedFonts.add(mcf);
+
+            try {
+
+                mcf.addFont(
+                    fontReference,
+                    font,
+                    size,
+                    orientation);
+
+            } catch (MaximumSizeExceededException ex) {
+
+                // Should never happen (but log just in case)
+                log.error("createFont():: resulted in a MaximumSizeExceededException");
+
+            }
+
+        }
+
+    }
+
+    /**
+     * Actually creates the MPO object.
+     * Also creates the supporting object (an IPO)
+     * @param name the name of the overlay to be used
+     */
+    public void createOverlay(String name) {
+
+        MapPageOverlay mpo = getCurrentMapPageOverlay();
+
+        if (mpo == null) {
+            mpo = new MapPageOverlay();
+            _mapPageOverlays.add(mpo);
+        }
+
+        try {
+
+            mpo.addOverlay(name);
+
+        } catch (MaximumSizeExceededException msee) {
+            mpo = new MapPageOverlay();
+            _mapPageOverlays.add(mpo);
+            try {
+                mpo.addOverlay(name);
+            } catch (MaximumSizeExceededException ex) {
+                // Should never happen (but log just in case)
+                log.error("createOverlay():: resulted in a MaximumSizeExceededException");
+            }
+        }
+    }
+
+    /**
+     * Getter method for the most recent MapCodedFont added to the
+     * Active Environment Group (returns null if no MapCodedFonts exist)
+     * @return the most recent Map Coded Font.
+     */
+    private MapCodedFont getCurrentMapCodedFont() {
+
+        int size = _mapCodedFonts.size();
+        if (size > 0) {
+            return (MapCodedFont) _mapCodedFonts.get(_mapCodedFonts.size() - 1);
+        } else {
+            return null;
+        }
+
+    }
+
+    /**
+     * Getter method for the most recent MapPageOverlay added to the
+     * Active Environment Group (returns null if no MapPageOverlay exist)
+     * @return the most recent Map Coded Font
+     */
+    private MapPageOverlay getCurrentMapPageOverlay() {
+
+        int size = _mapPageOverlays.size();
+        if (size > 0) {
+            return (MapPageOverlay) _mapPageOverlays.get(
+                _mapPageOverlays.size() - 1);
+        } else {
+            return null;
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/Document.java b/src/sandbox/org/apache/fop/render/afp/modca/Document.java
new file mode 100644 (file)
index 0000000..10e5a4b
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+
+/**
+ * The document is the highest level of the MO:DCA data-stream document
+ * component hierarchy. Documents can be made up of pages, and the pages,
+ * which are at the intermediate level, can be made up of objects. Objects
+ * are at the lowest level, and can be bar codes, graphics, images, and
+ * presentation text.
+ *
+ * At each level of the hierarchy certain sets of MO:DCA data structures,
+ * called structured fields, are permissible. The document, pages and objects
+ * are bounded by structured fields that define their beginnings and their ends.
+ * These structured fields, called begin-end pairs, provide an envelope for the
+ * data-stream components. This feature enables a processor of the data stream
+ * that is not fully compliant with the architecture to bypass those objects
+ * that are beyond its scope, and to process the data stream to the best of its
+ * abilities.
+ *
+ * A presentation document is one that has been formatted and is intended for
+ * presentation, usually on a printer or display device. A data stream containing
+ * a presentation document should produce the same document content in the
+ * same format on different printers or display devices dependent, however,
+ * on the capabilities of each of the printers or display devices. A presentation
+ * document can reference resources that are to be included as part of the
+ * document to be presented.
+ *
+ */
+public final class Document extends AbstractNamedAFPObject {
+
+    /**
+     * Ststic default name reference
+     */
+    private final static String DEFAULT_NAME = "DOC00001";
+
+    /**
+     * A list of the objects in the document
+     */
+    private ArrayList _objects = new ArrayList();
+
+    /**
+     * The document started state
+     */
+    private boolean _started = false;
+
+    /**
+     * The document completion state
+     */
+    private boolean _complete = false;
+
+    /**
+     * Default constructor for the document object.
+     */
+    public Document() {
+        this(DEFAULT_NAME);
+    }
+
+    /**
+     * Constructor for the document object.
+     * @param name The name of the document
+     */
+    public Document(String name) {
+
+        super(name);
+
+    }
+
+    /**
+     * Adds a page to the document.
+     * @param page - the Page object
+     */
+    public void addPage(PageObject page) {
+        if (!_objects.contains(page)) {
+            _objects.add(page);
+        }
+    }
+
+    /**
+     * Adds a PageGroup to the document.
+     * @param pageGroup the PageGroup object
+     */
+    public void addPageGroup(PageGroup pageGroup) {
+        _objects.add(pageGroup);
+    }
+
+    /**
+     * Method to mark the end of the page group.
+     */
+    public void endDocument() {
+
+        _complete = true;
+
+    }
+
+    /**
+     * Returns an indication if the page group is complete
+     */
+    public boolean isComplete() {
+        return _complete;
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for document.
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        if (!_started) {
+            writeStart(os);
+            _started = true;
+        }
+
+        for (Iterator it = _objects.iterator(); it.hasNext(); ) {
+            AbstractAFPObject ao = (AbstractAFPObject)it.next();
+            if (ao instanceof PageObject && ((PageObject)ao).isComplete()
+                || ao instanceof PageGroup && ((PageGroup)ao).isComplete()) {
+                ao.writeDataStream(os);
+                it.remove();
+            } else {
+                break;
+            }
+        }
+
+        if (_complete) {
+            writeEnd(os);
+        }
+
+    }
+
+    /**
+     * Helper method to write the start of the Document
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA8; // Structured field id byte 2
+        data[5] = (byte) 0xA8; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the Document.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA9; // Structured field id byte 2
+        data[5] = (byte) 0xA8; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/EndPageGroup.java b/src/sandbox/org/apache/fop/render/afp/modca/EndPageGroup.java
new file mode 100644 (file)
index 0000000..afa720d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * The End Named Page Group (ENG) structured field terminates a page group that was
+ * initiated by a Begin Named Page Group structured field.
+ *
+ * Note :This object will be used to represent an ENG
+ * structured field. It is necessary as you can't end
+ * a PageGroup because you don't know where the group
+ * will end (as this is controlled by the tags in the FO).
+ * <p>
+ *
+ */
+public class EndPageGroup extends AbstractNamedAFPObject {
+
+    public EndPageGroup(String groupId) {
+
+        super(groupId);
+
+        log.debug("A ENG is being created for group: " + groupId);
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the End Page Group.
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA9; // Structured field id byte 2
+        data[5] = (byte) 0xAD; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/IMImageObject.java b/src/sandbox/org/apache/fop/render/afp/modca/IMImageObject.java
new file mode 100644 (file)
index 0000000..aa510da
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * An IM image data object specifies the contents of a raster image and
+ * its placement on a page, overlay, or page segment. An IM image can be
+ * either simple or complex. A simple image is composed of one or more Image
+ * Raster Data (IRD) structured fields that define the raster pattern for the
+ * entire image. A complex image is divided into regions called image cells.
+ * Each image cell is composed of one or more IRD structured fields that define
+ * the raster pattern for the image cell, and one Image Cell Position (ICP)
+ * structured field that defines the position of the image cell relative to
+ * the origin of the entire image. Each ICP also specifies the size of the
+ * image cell and a fill rectangle into which the cell is replicated.
+ * <p/>
+ */
+public class IMImageObject extends AbstractNamedAFPObject {
+
+    /**
+     * The image output control
+     */
+    private ImageOutputControl _imageOutputControl = null;
+
+    /**
+     * The image input descriptor
+     */
+    private ImageInputDescriptor _imageInputDescriptor = null;
+
+    /**
+     * The image cell position
+     */
+    private ImageCellPosition _imageCellPosition = null;
+
+    /**
+     * The image rastor data
+     */
+    private ImageRasterData _imageRastorData = null;
+
+    /**
+     * Constructor for the image object with the specified name,
+     * the name must be a fixed length of eight characters.
+     * @param name     The name of the image.
+     */
+    public IMImageObject(String name) {
+
+        super(name);
+
+    }
+
+    /**
+     * Sets the ImageOutputControl.
+     * @param imageOutputControl The imageOutputControl to set
+     */
+    public void setImageOutputControl(ImageOutputControl imageOutputControl) {
+        _imageOutputControl = imageOutputControl;
+    }
+
+    /**
+     * Sets the ImageCellPosition.
+     * @param imageCellPosition The imageCellPosition to set
+     */
+    public void setImageCellPosition(ImageCellPosition imageCellPosition) {
+        _imageCellPosition = imageCellPosition;
+    }
+
+    /**
+     * Sets the ImageInputDescriptor.
+     * @param imageInputDescriptor The imageInputDescriptor to set
+     */
+    public void setImageInputDescriptor(ImageInputDescriptor imageInputDescriptor) {
+        _imageInputDescriptor = imageInputDescriptor;
+    }
+
+    /**
+     * Sets the ImageRastorData.
+     * @param imageRastorData The imageRastorData to set
+     */
+    public void setImageRasterData(ImageRasterData imageRastorData) {
+        _imageRastorData = imageRastorData;
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the IM Image Objetc
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        writeStart(os);
+
+        if (_imageOutputControl != null) {
+            _imageOutputControl.writeDataStream(os);
+        }
+
+        if (_imageInputDescriptor != null) {
+            _imageInputDescriptor.writeDataStream(os);
+        }
+
+        if (_imageCellPosition != null) {
+            _imageCellPosition.writeDataStream(os);
+        }
+
+        if (_imageRastorData != null) {
+            _imageRastorData.writeDataStream(os);
+        }
+
+        writeEnd(os);
+
+    }
+
+    /**
+     * Helper method to write the start of the IM Image Object.
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA8; // Structured field id byte 2
+        data[5] = (byte) 0x7B; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the IM Image Object.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA9; // Structured field id byte 2
+        data[5] = (byte) 0x7B; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ImageCellPosition.java b/src/sandbox/org/apache/fop/render/afp/modca/ImageCellPosition.java
new file mode 100644 (file)
index 0000000..aa97e58
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * The IM Image Cell Position structured field specifies the placement,
+ * size, and replication of IM image cells.
+ */
+public class ImageCellPosition extends AbstractAFPObject {
+
+    /**
+     * Offset of image cell in X direction
+     */
+    private int _XcoSet = 0;
+
+    /**
+     * Offset of image cell in Y direction
+     */
+    private int _YcoSet = 0;
+
+    /**
+     * Size of image cell in X direction
+     */
+    private byte[] _XcSize = new byte[] { (byte)0xFF, (byte)0xFF };
+
+    /**
+     * Size of image cell in Y direction
+     */
+    private byte[] _YcSize = new byte[] { (byte)0xFF, (byte)0xFF };
+
+    /**
+     * Size of fill rectangle in X direction
+     */
+    private byte[] _XFillSize = new byte[] { (byte)0xFF, (byte)0xFF };
+
+    /**
+     * Size of fill rectangle in Y direction
+     */
+    private byte[] _YFillSize = new byte[] { (byte)0xFF, (byte)0xFF };
+
+    /**
+     * Constructor for the ImageCellPosition
+     * @param x The offset of image cell in X direction
+     * @param y The offset of image cell in Y direction
+     */
+    public ImageCellPosition(int x, int y) {
+
+        _XcoSet = x;
+        _YcoSet = y;
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Image Cell Position
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[21];
+
+        data[0] = 0x5A;
+
+        data[1] = 0x00;
+        data[2] = 0x14;
+
+        data[3] = (byte) 0xD3;
+        data[4] = (byte) 0xAC;
+        data[5] = (byte) 0x7B;
+        data[6] = 0x00;
+        data[7] = 0x00;
+        data[8] = 0x00;
+
+        /**
+         * Specifies the offset along the Xp direction, in image points,
+         * of this image cell from the IM image object area origin.
+         */
+        byte[] x1 = BinaryUtils.convert(_XcoSet, 2);
+        data[9] = x1[0];
+        data[10] = x1[1];
+
+        /**
+         * Specifies the offset along the Yp direction, in image points,
+         * of this image cell from the IM image object area origin.
+         */
+        byte[] x2 = BinaryUtils.convert(_YcoSet, 2);
+        data[11] = x2[0];
+        data[12] = x2[1];
+
+        data[13] = _XcSize[0];
+        data[14] = _XcSize[1];
+
+        data[15] = _YcSize[0];
+        data[16] = _YcSize[1];
+
+        data[17] = _XFillSize[0];
+        data[18] = _XFillSize[1];
+
+        data[19] = _YFillSize[0];
+        data[20] = _YFillSize[1];
+
+        os.write(data);
+
+    }
+
+    /**
+     * Specifies the extent in the X direction, in image points,
+     * of this image cell. A value of X'FFFF' indicates that the
+     * default extent specified in bytes 28 and 29 of the Image
+     * Input Descriptor (IID) is to be used.
+     * @param xcSize The size to set.
+     */
+    public void setXSize(int xcSize) {
+
+        byte[] x = BinaryUtils.convert(xcSize, 2);
+        _XcSize[0] = x[0];
+        _XcSize[1] = x[1];
+
+    }
+
+    /**
+     * Specifies the extent of the fill rectangle in the X direction,
+     * in image points. This value can be smaller than, equal to, or
+     * larger than the image cell extent in the X direction (XCSize).
+     * A value of X'FFFF' indicates that the image cell X-extent should
+     * be used as the fill rectangle X-extent. The fill rectangle is
+     * filled in the X direction by repeating the image cell in the
+     * X direction. The image cell can be truncated to fit the rectangle.
+     * @param xFillSize The size to set.
+     */
+    public void setXFillSize(int xFillSize) {
+
+        byte[] x = BinaryUtils.convert(xFillSize, 2);
+        _XFillSize[0] = x[0];
+        _XFillSize[1] = x[1];
+
+    }
+
+    /**
+     * Specifies the extent in the Y direction, in image points,
+     * of this image cell. A value of X'FFFF' indicates that the
+     * default extent specified in bytes 30 and 31 of the Image
+     * Input Descriptor (IID) is to be used.
+     * @param ycSize The size to set.
+     */
+    public void setYSize(int ycSize) {
+
+        byte[] x = BinaryUtils.convert(ycSize, 2);
+        _YcSize[0] = x[0];
+        _YcSize[1] = x[1];
+
+    }
+
+    /**
+     * Specifies the extent of the fill rectangle in the Y direction,
+     * in image points. This value can be smaller than, equal to, or
+     * larger than the image cell extent in the Y direction (YCSize).
+     * A value of X'FFFF' indicates that the image cell Y-extent should
+     * be used as the fill rectangle Y-extent. The fill rectangle is
+     * filled in the Y direction by repeating the image cell in the
+     * Y direction. The image cell can be truncated to fit the rectangle.
+     * @param yFillSize The size to set.
+     */
+    public void setYFillSize(int yFillSize) {
+
+        byte[] x = BinaryUtils.convert(yFillSize, 2);
+        _YFillSize[0] = x[0];
+        _YFillSize[1] = x[1];
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ImageContent.java b/src/sandbox/org/apache/fop/render/afp/modca/ImageContent.java
new file mode 100644 (file)
index 0000000..9479f47
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ */
+public class ImageContent extends AbstractAFPObject {
+
+    /**
+     * The image size parameter
+     */
+    private ImageSizeParameter _imageSizeParameter = null;
+
+    /**
+     * The image encoding
+     */
+    private byte _encoding = 0x03;
+
+    /**
+     * The image ide size
+     */
+    private byte _size = 1;
+
+    /**
+     * The image compression
+     */
+    private byte _compression = (byte)0xC0;
+
+    /**
+     * The image color model
+     */
+    private byte _colorModel = 0x01;
+
+    /**
+     * The image data
+     */
+    private byte _data[] = null;
+
+    /**
+     * Constructor for the image content
+     */
+    public ImageContent() {
+
+    }
+
+    /**
+     * Sets the image size parameters
+     * resolution, hsize and vsize.
+     * @param hresol The horizontal resolution of the image.
+     * @param vresol The vertical resolution of the image.
+     * @param hsize The horizontal size of the image.
+     * @param vsize The vertival size of the image.
+     */
+    public void setImageSize(int hresol, int vresol, int hsize, int vsize) {
+        _imageSizeParameter = new ImageSizeParameter(hresol, vresol, hsize, vsize);
+    }
+
+    /**
+     * Sets the image encoding.
+     * @param encoding The image encoding.
+     */
+    public void setImageEncoding(byte encoding) {
+        _encoding = encoding;
+    }
+
+    /**
+     * Sets the image compression.
+     * @param compression The image compression.
+     */
+    public void setImageCompression(byte compression) {
+        _compression = compression;
+    }
+
+    /**
+     * Sets the image IDE size.
+     * @param size The IDE size.
+     */
+    public void setImageIDESize(byte size) {
+        _size = size;
+    }
+
+    /**
+     * Sets the image IDE color model.
+     * @param size The IDE color model.
+     */
+    public void setImageIDEColorModel(byte colorModel) {
+        _colorModel = colorModel;
+    }
+
+    /**
+     * Set the data of the image.
+     */
+    public void setImageData(byte data[]) {
+        _data = data;
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Image Content
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        writeStart(os);
+
+        if (_imageSizeParameter != null) {
+            _imageSizeParameter.writeDataStream(os);
+        }
+
+        os.write(getImageEncodingParameter());
+
+        os.write(getImageIDESizeParameter());
+
+        os.write(getIDEStructureParameter());
+
+        os.write(getExternalAlgorithmParameter());
+
+        if (_data != null) {
+            int off = 0;
+            while (off < _data.length) {
+                int len = Math.min(30000, _data.length - off);
+                os.write(getImageDataStart(len));
+                os.write(_data, off, len);
+                off += len;
+            }
+        }
+
+        writeEnd(os);
+
+    }
+
+    /**
+     * Helper method to write the start of the Image Content.
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[] {
+            (byte)0x91, // ID
+                  0x01, // Length
+            (byte)0xff, // Object Type = IOCA Image Object
+        };
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the Image Content.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[] {
+            (byte)0x93, // ID
+                  0x00, // Length
+        };
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to return the start of the image segment.
+     * @return byte[] The data stream.
+     */
+    private byte[] getImageDataStart(int len) {
+
+        byte[] data = new byte[] {
+            (byte)0xFE, // ID
+            (byte)0x92, // ID
+                  0x00, // Length
+                  0x00, // Length
+        };
+
+        byte[] l = BinaryUtils.convert(len, 2);
+        data[2] = l[0];
+        data[3] = l[1];
+
+
+        return data;
+
+    }
+
+    /**
+     * Helper method to return the image encoding parameter.
+     * @return byte[] The data stream.
+     */
+    private byte[] getImageEncodingParameter() {
+
+        byte[] data = new byte[] {
+            (byte)0x95, // ID
+                  0x02, // Length
+                  _encoding,
+                  0x01, // RECID
+        };
+
+        return data;
+
+    }
+
+    /**
+     * Helper method to return the external algorithm parameter.
+     * @return byte[] The data stream.
+     */
+    private byte[] getExternalAlgorithmParameter() {
+
+        if (_encoding == (byte)0x83 && _compression != 0) {
+            byte[] data = new byte[] {
+                (byte)0x95, // ID
+                      0x00, // Length
+                      0x10, // ALGTYPE = Compression Algorithm
+                      0x00, // Reserved
+                (byte)0x83, // COMPRID = JPEG
+                      0x00, // Reserved
+                      0x00, // Reserved
+                      0x00, // Reserved
+              _compression, // MARKER
+                      0x00, // Reserved
+                      0x00, // Reserved
+                      0x00, // Reserved
+            };
+            data[1] = (byte)(data.length - 2);
+            return data;
+        }
+        return new byte[0];
+    }
+
+    /**
+     * Helper method to return the image encoding parameter.
+     * @return byte[] The data stream.
+     */
+    private byte[] getImageIDESizeParameter() {
+
+        byte[] data = new byte[] {
+            (byte)0x96, // ID
+                  0x01, // Length
+                  _size,
+        };
+
+        return data;
+
+    }
+
+    /**
+     * Helper method to return the external algorithm parameter.
+     * @return byte[] The data stream.
+     */
+    private byte[] getIDEStructureParameter() {
+
+        if (_colorModel != 0 && _size == 24) {
+            byte bits = (byte)(_size / 3);
+            byte[] data = new byte[] {
+                (byte)0x9B, // ID
+                      0x00, // Length
+                      0x00, // FLAGS
+                      0x00, // Reserved
+               _colorModel, // COLOR MODEL
+                      0x00, // Reserved
+                      0x00, // Reserved
+                      0x00, // Reserved
+                      bits,
+                      bits,
+                      bits,
+            };
+            data[1] = (byte)(data.length - 2);
+            return data;
+        }
+        return new byte[0];
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ImageDataDescriptor.java b/src/sandbox/org/apache/fop/render/afp/modca/ImageDataDescriptor.java
new file mode 100644 (file)
index 0000000..25f06c7
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ */
+public class ImageDataDescriptor extends AbstractAFPObject {
+
+    private int _xresol = 0;
+    private int _yresol = 0;
+    private int _width = 0;
+    private int _height = 0;
+
+    /**
+     * Constructor for a ImageDataDescriptor for the specified
+     * resolution, width and height.
+     * @param xresol The horizontal resolution of the image.
+     * @param yresol The vertical resolution of the image.
+     * @param width The width of the image.
+     * @param height The height of the height.
+     */
+    public ImageDataDescriptor(int xresol, int yresol, int width, int height) {
+
+        _xresol = xresol;
+        _yresol = yresol;
+        _width = width;
+        _height = height;
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Image Data Descriptor
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[] {
+            0x5A,
+            0x00,
+            0x20,
+            (byte) 0xD3,
+            (byte) 0xA6,
+            (byte) 0xFB,
+            0x00, // Flags
+            0x00, // Reserved
+            0x00, // Reserved
+            0x00, // Unit base - 10 Inches
+            0x00, // XRESOL
+            0x00, //
+            0x00, // YRESOL
+            0x00, //
+            0x00, // XSIZE
+            0x00, //
+            0x00, // YSIZE
+            0x00, //
+            (byte)0xF7, // ID = Set IOCA Function Set
+            0x02, // Length
+            0x01, // Category = Function set identifier
+            0x0B, // FCNSET = IOCA FS 11
+        };
+
+        byte[] l = BinaryUtils.convert(data.length - 1, 2);
+        data[1] = l[0];
+        data[2] = l[1];
+
+        byte[] x = BinaryUtils.convert(_xresol, 2);
+        data[10] = x[0];
+        data[11] = x[1];
+
+        byte[] y = BinaryUtils.convert(_yresol, 2);
+        data[12] = y[0];
+        data[13] = y[1];
+
+        byte[] w = BinaryUtils.convert(_width, 2);
+        data[14] = w[0];
+        data[15] = w[1];
+
+        byte[] h = BinaryUtils.convert(_height, 2);
+        data[16] = h[0];
+        data[17] = h[1];
+
+        os.write(data);
+
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ImageInputDescriptor.java b/src/sandbox/org/apache/fop/render/afp/modca/ImageInputDescriptor.java
new file mode 100644 (file)
index 0000000..eeef6b7
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * The IM Image Input Descriptor structured field contains the
+ * descriptor data for an IM image data object. This data specifies
+ * the resolution, size, and color of the IM image.
+ */
+public class ImageInputDescriptor extends AbstractAFPObject {
+
+    /**
+     * The resolution of the raster image (default 240)
+     */
+    private int _resolution = 240;
+
+
+    /**
+     * Accessor method to write the AFP datastream for the Image Input Descriptor
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[45];
+
+        data[0] = 0x5A;
+        data[1] = 0x00;
+        data[2] = 0x2C;
+        data[3] = (byte) 0xD3;
+        data[4] = (byte) 0xA6;
+        data[5] = (byte) 0x7B;
+        data[6] = 0x00;
+        data[7] = 0x00;
+        data[8] = 0x00;
+
+        // Constant data.
+        data[9] = 0x00;
+        data[10] = 0x00;
+        data[11] = 0x09;
+        data[12] = 0x60;
+        data[13] = 0x09;
+        data[14] = 0x60;
+        data[15] = 0x00;
+        data[16] = 0x00;
+        data[17] = 0x00;
+        data[18] = 0x00;
+        data[19] = 0x00;
+        data[20] = 0x00;
+
+        // X Base (Fixed x00)
+        data[21] = 0x00;
+        // Y Base (Fixed x00)
+        data[22] = 0x00;
+
+        byte[] imagepoints = BinaryUtils.convert(_resolution * 10, 2);
+
+        /**
+         * Specifies the number of image points per unit base for the X axis
+         * of the image. This value is ten times the resolution of the image
+         * in the X direction.
+         */
+        data[23] = imagepoints[0];
+        data[24] = imagepoints[1];
+
+        /**
+         * Specifies the number of image points per unit base for the Y axis
+         * of the image. This value is ten times the resolution of the image
+         * in the Y direction.
+         */
+        data[25] = imagepoints[0];
+        data[26] = imagepoints[1];
+
+        /**
+         * Specifies the extent in the X direction, in image points, of an
+         * non-celled (simple) image.
+         */
+        data[27] = 0x00;
+        data[28] = 0x01;
+
+        /**
+         * Specifies the extent in the Y direction, in image points, of an
+         * non-celled (simple) image.
+         */
+        data[29] = 0x00;
+        data[30] = 0x01;
+
+        // Constant Data
+        data[31] = 0x00;
+        data[32] = 0x00;
+        data[33] = 0x00;
+        data[34] = 0x00;
+        data[35] = 0x2D;
+        data[36] = 0x00;
+
+        // Default size of image cell in X direction
+        data[37] = 0x00;
+        data[38] = 0x01;
+
+        // Default size of image cell in Y direction
+        data[39] = 0x00;
+        data[40] = 0x01;
+
+        // Constant Data
+        data[41] = 0x00;
+        data[42] = 0x01;
+
+        // Image Color
+        data[43] = (byte)0xFF;
+        data[44] = (byte)0xFF;
+
+        os.write(data);
+
+    }
+
+    /**
+     * Sets the resolution information for the raster image
+     * the default value is a resolution of 240 dpi.
+     * @param resolution The resolution value
+     */
+    public void setResolution(int resolution) {
+        _resolution = resolution;
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ImageObject.java b/src/sandbox/org/apache/fop/render/afp/modca/ImageObject.java
new file mode 100644 (file)
index 0000000..8e4e4c3
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * An IOCA Image Data Object
+ */
+public class ImageObject extends AbstractNamedAFPObject {
+
+    /**
+     * The object environment group
+     */
+    private ObjectEnvironmentGroup _objectEnvironmentGroup = null;
+
+    /**
+     * The image segment
+     */
+    private ImageSegment _imageSegment = null;
+
+    /**
+     * Constructor for the image object with the specified name,
+     * the name must be a fixed length of eight characters.
+     * @param name     The name of the image.
+     */
+    public ImageObject(String name) {
+
+        super(name);
+
+    }
+
+    /**
+     * Sets the image display area position and size.
+     *
+     * @param x
+     *            the x position of the image
+     * @param y
+     *            the y position of the image
+     * @param w
+     *            the width of the image
+     * @param h
+     *            the height of the image
+     * @param r
+     *            the rotation of the image
+     */
+    public void setImageViewport(int x, int y, int w, int h, int r) {
+        if (_objectEnvironmentGroup == null) {
+            _objectEnvironmentGroup = new ObjectEnvironmentGroup();
+        }
+        _objectEnvironmentGroup.setObjectArea(x, y, w, h, r);
+    }
+
+    /**
+     * Set the dimensions of the image.
+     * @param xresol the x resolution of the image
+     * @param yresol the y resolution of the image
+     * @param width the image width
+     * @param height the image height
+     */
+    public void setImageParameters(int xresol, int yresol, int width, int height) {
+        if (_objectEnvironmentGroup == null) {
+            _objectEnvironmentGroup = new ObjectEnvironmentGroup();
+        }
+        _objectEnvironmentGroup.setImageData(xresol, yresol, width, height);
+        if (_imageSegment == null) {
+            _imageSegment = new ImageSegment();
+        }
+        _imageSegment.setImageSize(xresol, yresol, width, height);
+    }
+
+    /**
+     * Sets the image encoding.
+     * @param encoding The image encoding.
+     */
+    public void setImageEncoding(byte encoding) {
+        if (_imageSegment == null) {
+            _imageSegment = new ImageSegment();
+        }
+        _imageSegment.setImageEncoding(encoding);
+    }
+
+    /**
+     * Sets the image compression.
+     * @param compression The image compression.
+     */
+    public void setImageCompression(byte compression) {
+        if (_imageSegment == null) {
+            _imageSegment = new ImageSegment();
+        }
+        _imageSegment.setImageCompression(compression);
+    }
+
+    /**
+     * Sets the image IDE size.
+     * @param size The IDE size.
+     */
+    public void setImageIDESize(byte size) {
+        if (_imageSegment == null) {
+            _imageSegment = new ImageSegment();
+        }
+        _imageSegment.setImageIDESize(size);
+    }
+
+    /**
+     * Sets the image IDE color model.
+     * @param size The IDE color model.
+     */
+    public void setImageIDEColorModel(byte colorModel) {
+        if (_imageSegment == null) {
+            _imageSegment = new ImageSegment();
+        }
+        _imageSegment.setImageIDEColorModel(colorModel);
+    }
+
+    /**
+     * Set the data of the image.
+     * @param data The image data
+     */
+    public void setImageData(byte data[]) {
+        if (_imageSegment == null) {
+            _imageSegment = new ImageSegment();
+        }
+        _imageSegment.setImageData(data);
+    }
+
+    /**
+     * Sets the ObjectEnvironmentGroup.
+     * @param objectEnvironmentGroup The objectEnvironmentGroup to set
+     */
+    public void setObjectEnvironmentGroup(ObjectEnvironmentGroup objectEnvironmentGroup) {
+        _objectEnvironmentGroup = objectEnvironmentGroup;
+    }
+
+    /**
+     * Helper method to return the start of the image object.
+     * @return byte[] The data stream.
+     */
+    private byte[] getIPDStart(int len) {
+
+        byte[] data = new byte[] {
+
+            0x5A, // Structured field identifier
+            0x00, // Length byte 1
+            0x10, // Length byte 2
+            (byte) 0xD3, // Structured field id byte 1
+            (byte) 0xEE, // Structured field id byte 2
+            (byte) 0xFB, // Structured field id byte 3
+            0x00, // Flags
+            0x00, // Reserved
+            0x00, // Reserved
+        };
+
+        byte[] l = BinaryUtils.convert(len + 8, 2);
+        data[1] = l[0];
+        data[2] = l[1];
+
+        return data;
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Image Object
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        writeStart(os);
+
+        if (_objectEnvironmentGroup != null) {
+            _objectEnvironmentGroup.writeDataStream(os);
+        }
+
+        if (_imageSegment != null) {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            _imageSegment.writeDataStream(baos);
+            byte b[] = baos.toByteArray();
+            int off = 0;
+            while (off < b.length) {
+                int len = Math.min(30000, b.length - off);
+                os.write(getIPDStart(len));
+                os.write(b, off, len);
+                off += len;
+            }
+        }
+
+        writeEnd(os);
+
+    }
+
+    /**
+     * Helper method to write the start of the Image Object.
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA8; // Structured field id byte 2
+        data[5] = (byte) 0xFB; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the Image Object.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA9; // Structured field id byte 2
+        data[5] = (byte) 0xFB; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ImageOutputControl.java b/src/sandbox/org/apache/fop/render/afp/modca/ImageOutputControl.java
new file mode 100644 (file)
index 0000000..8a2aaae
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * The IM Image Output Control structured field specifies the position and
+ * orientation of the IM image object area and the mapping of the image points
+ * to presentation device pels.
+ *
+ */
+public class ImageOutputControl extends AbstractAFPObject {
+
+    /**
+     * The orientation of the image
+     */
+    private int _orientation = 0;
+
+    /**
+     * Specifies the offset, along the X-axis, of the IM image object area
+     * origin to the origin of the including page
+     */
+    private int _Xcoordinate = 0;
+
+    /**
+     * Specifies the offset, along the Y-axis, of the IM image object area
+     * origin to the origin of the including page
+     */
+    private int _Ycoordinate = 0;
+
+    /**
+     * Map an image point to a single presentation device pel
+     */
+    private boolean _singlepoint = true;
+
+    /**
+     * Constructor for the ImageOutputControl The x parameter specifies the
+     * offset, along the X-axis, of the IM image object area origin to the
+     * origin of the including page and the y parameter specifies the offset
+     * along the Y-axis. The offset is specified in image points and is resolved
+     * using the units of measure specified for the image in the IID structured
+     * field.
+     *
+     * @param x
+     *            The X-axis offset.
+     * @param y
+     *            The Y-axis offset.
+     */
+    public ImageOutputControl(int x, int y) {
+
+        _Xcoordinate = x;
+        _Ycoordinate = y;
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Image Output Control
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[33];
+
+        data[0] = 0x5A;
+        data[1] = 0x00;
+        data[2] = 0x20;
+        data[3] = (byte) 0xD3;
+        data[4] = (byte) 0xA7;
+        data[5] = (byte) 0x7B;
+        data[6] = 0x00;
+        data[7] = 0x00;
+        data[8] = 0x00;
+
+        // XoaOset
+        byte[] x1 = BinaryUtils.convert(_Xcoordinate, 3);
+        data[9] = x1[0];
+        data[10] = x1[1];
+        data[11] = x1[2];
+
+        // YoaOset
+        byte[] x2 = BinaryUtils.convert(_Ycoordinate, 3);
+        data[12] = x2[0];
+        data[13] = x2[1];
+        data[14] = x2[2];
+
+        switch (_orientation) {
+            case 0:
+                // 0 and 90 degrees respectively
+                data[15] = 0x00;
+                data[16] = 0x00;
+                data[17] = 0x2D;
+                data[18] = 0x00;
+                break;
+            case 90:
+                // 90 and 180 degrees respectively
+                data[15] = 0x2D;
+                data[16] = 0x00;
+                data[17] = 0x5A;
+                data[18] = 0x00;
+                break;
+            case 180:
+                // 180 and 270 degrees respectively
+                data[15] = 0x5A;
+                data[16] = 0x00;
+                data[17] = (byte) 0x87;
+                data[18] = 0x00;
+                break;
+            case 270:
+                // 270 and 0 degrees respectively
+                data[15] = (byte) 0x87;
+                data[16] = 0x00;
+                data[17] = 0x00;
+                data[18] = 0x00;
+                break;
+            default:
+                // 0 and 90 degrees respectively
+                data[15] = 0x00;
+                data[16] = 0x00;
+                data[17] = 0x2D;
+                data[18] = 0x00;
+                break;
+
+        }
+
+        // Constant Data
+        data[19] = 0x00;
+        data[20] = 0x00;
+        data[21] = 0x00;
+        data[22] = 0x00;
+        data[23] = 0x00;
+        data[24] = 0x00;
+        data[25] = 0x00;
+        data[26] = 0x00;
+
+        if (_singlepoint) {
+            data[27] = 0x03;
+            data[28] = (byte) 0xE8;
+            data[29] = 0x03;
+            data[30] = (byte) 0xE8;
+        } else {
+            data[27] = 0x07;
+            data[28] = (byte) 0xD0;
+            data[29] = 0x07;
+            data[30] = (byte) 0xD0;
+        }
+
+        // Constant Data
+        data[31] = (byte) 0xFF;
+        data[32] = (byte) 0xFF;
+
+        os.write(data);
+
+    }
+
+    /**
+     * Sets the orientation which specifies the amount of clockwise rotation of
+     * the IM image object area.
+     *
+     * @param orientation
+     *            The orientation to set.
+     */
+    public void setOrientation(int orientation) {
+
+        if (orientation == 0 || orientation == 90 || orientation == 180
+            || orientation == 270) {
+            _orientation = orientation;
+        } else {
+            throw new IllegalArgumentException(
+                "The orientation must be one of the values 0, 90, 180, 270");
+        }
+
+    }
+
+    /**
+     * Sets the singlepoint, if true map an image point to a single presentation
+     * device pel in the IM image object area. If false map an image point to
+     * two presentation device pels in the IM image object area (double-dot)
+     *
+     * @param singlepoint
+     *            Use the singlepoint basis when true.
+     */
+    public void setSinglepoint(boolean singlepoint) {
+        _singlepoint = singlepoint;
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ImageRasterData.java b/src/sandbox/org/apache/fop/render/afp/modca/ImageRasterData.java
new file mode 100644 (file)
index 0000000..9c815cb
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * Contains the image points that define the IM image raster pattern.
+ *
+ * A raster pattern is the array of presentation device pels that forms
+ * the image. The image data is uncompressed. Bits are grouped into
+ * bytes and are ordered from left to right within each byte. Each bit
+ * in the image data represents an image point and is mapped to
+ * presentation device pels as specified in the IOC structured field.
+ * A bit with value B'1' indicates a significant image point; a bit
+ * with value B'0' indicates an insignificant image point.
+ * Image points are recorded from left to right in rows that represents
+ * scan lines (X direction), and rows representing scan lines are
+ * recorded from top to bottom (Y direction). When the image is
+ * presented, all image points in a row are presented before any
+ * image points in the next sequential row are presented, and all rows
+ * have the same number of image points. If the total number of image
+ * points is not a multiple of 8, the last byte of the image data is
+ * padded to a byte boundary. The padding bits do not represent image
+ * points and are ignored by presentation devices.
+ */
+public class ImageRasterData extends AbstractAFPObject {
+
+    /**
+     * The image raster data
+     */
+    private byte[] _rasterdata;
+
+    /**
+     * Constructor for the image raster data object
+     * @param rasterdata The raster image data
+     */
+    public ImageRasterData(byte[] rasterdata) {
+
+        _rasterdata = rasterdata;
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Image Raster Data
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[9];
+
+        data[0] = 0x5A;
+
+        // The size of the structured field
+        byte[] x = BinaryUtils.convert(_rasterdata.length + 8, 2);
+        data[1] = x[0];
+        data[2] = x[1];
+
+        data[3] = (byte) 0xD3;
+        data[4] = (byte) 0xEE;
+        data[5] = (byte) 0x7B;
+        data[6] = 0x00;
+        data[7] = 0x00;
+        data[8] = 0x00;
+
+        os.write(data);
+        os.write(_rasterdata);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ImageRasterPattern.java b/src/sandbox/org/apache/fop/render/afp/modca/ImageRasterPattern.java
new file mode 100644 (file)
index 0000000..c49f392
--- /dev/null
@@ -0,0 +1,762 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+/**
+ * Raster data is a grid of cells covering an area of interest.
+ * Each pixel, the smallest unit of information in the grid, displays
+ * a unique attribute. This static class generates raster data for different
+ * shades of grey (betweeen 0 and 16) the lower the number being the
+ * darker the shade. The image data dimensions are 64 x 8.
+ */
+public class ImageRasterPattern {
+
+    /**
+     * The Raster Pattern for Greyscale 16
+     */
+    private static final byte[] GREYSCALE16 = new byte[] {
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+    };
+
+    /**
+     * The Raster Pattern for Greyscale 15
+     */
+    private static final byte[] GREYSCALE15 = new byte[] {
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+    };
+
+    /**
+     * The Raster Pattern for Greyscale 14
+     */
+    private static final byte[] GREYSCALE14 = new byte[] {
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+    };
+
+
+    /**
+     * The Raster Pattern for Greyscale 13
+     */
+    private static final byte[] GREYSCALE13 = new byte[] {
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+    };
+
+    /**
+     * The Raster Pattern for Greyscale 12
+     */
+    private static final byte[] GREYSCALE12 = new byte[] {
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+    };
+
+    /**
+     * The Raster Pattern for Greyscale 11
+     */
+    private static final byte[] GREYSCALE11 = new byte[] {
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+    };
+
+    /**
+     * The Raster Pattern for Greyscale 10
+     */
+    private static final byte[] GREYSCALE10 = new byte[] {
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            0x44,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+    };
+
+    /**
+     * The Raster Pattern for Greyscale 9
+     */
+    private static final byte[] GREYSCALE09 = new byte[] {
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            0x11,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+    };
+
+    /**
+     * The Raster Pattern for Greyscale 8
+     */
+    private static final byte[] GREYSCALE08 = new byte[] {
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+    };
+
+
+    /**
+     * The Raster Pattern for Greyscale 7
+     */
+    private static final byte[] GREYSCALE07 = new byte[] {
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+    };
+
+
+    /**
+     * The Raster Pattern for Greyscale 6
+     */
+    private static final byte[] GREYSCALE06 = new byte[] {
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+    };
+
+    /**
+     * The Raster Pattern for Greyscale 5
+     */
+    private static final byte[] GREYSCALE05 = new byte[] {
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xEE,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+    };
+
+
+    /**
+     * The Raster Pattern for Greyscale 4
+     */
+    private static final byte[] GREYSCALE04 = new byte[] {
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xAA,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+    };
+
+    /**
+     * The Raster Pattern for Greyscale 3
+     */
+    private static final byte[] GREYSCALE03 = new byte[] {
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            0x55,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xBB,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+    };
+
+    /**
+     * The Raster Pattern for Greyscale 2
+     */
+    private static final byte[] GREYSCALE02 = new byte[] {
+            0x77,
+            0x77,
+            0x77,
+            0x77,
+            0x77,
+            0x77,
+            0x77,
+            0x77,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xDD,
+            (byte)0xDD,
+            (byte)0xDD,
+            (byte)0xDD,
+            (byte)0xDD,
+            (byte)0xDD,
+            (byte)0xDD,
+            (byte)0xDD,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+    };
+
+
+    /**
+     * The Raster Pattern for Greyscale 1
+     */
+    private static final byte[] GREYSCALE01 = new byte[] {
+            0x77,
+            0x77,
+            0x77,
+            0x77,
+            0x77,
+            0x77,
+            0x77,
+            0x77,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+    };
+
+
+    /**
+     * The Raster Pattern for Greyscale 00
+     */
+    private static final byte[] GREYSCALE00 = new byte[] {
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+            (byte)0xFF,
+    };
+
+    /**
+     * Static method to return the raster image data for the
+     * grey scale specified. The scale should be between 0 (darkest)
+     * and 16 (lightest).
+     * @param greyscale The grey scale value (0 - 16)
+     */
+    public static byte[] getRasterData(int greyscale) {
+
+        int repeat = 16;
+
+        byte[] greypattern = new byte[32];
+        byte[] rasterdata = new byte[32 * repeat];
+
+        switch (greyscale) {
+            case 0:
+                System.arraycopy(GREYSCALE00, 0, greypattern, 0, 32);
+                break;
+            case 1:
+                System.arraycopy(GREYSCALE01, 0, greypattern, 0, 32);
+                break;
+            case 2:
+                System.arraycopy(GREYSCALE02, 0, greypattern, 0, 32);
+                break;
+            case 3:
+                System.arraycopy(GREYSCALE03, 0, greypattern, 0, 32);
+                break;
+            case 4:
+                System.arraycopy(GREYSCALE04, 0, greypattern, 0, 32);
+                break;
+            case 5:
+                System.arraycopy(GREYSCALE05, 0, greypattern, 0, 32);
+                break;
+            case 6:
+                System.arraycopy(GREYSCALE06, 0, greypattern, 0, 32);
+                break;
+            case 7:
+                System.arraycopy(GREYSCALE07, 0, greypattern, 0, 32);
+                break;
+            case 8:
+                System.arraycopy(GREYSCALE08, 0, greypattern, 0, 32);
+                break;
+            case 9:
+                System.arraycopy(GREYSCALE09, 0, greypattern, 0, 32);
+                break;
+            case 10:
+                System.arraycopy(GREYSCALE10, 0, greypattern, 0, 32);
+                break;
+            case 11:
+                System.arraycopy(GREYSCALE11, 0, greypattern, 0, 32);
+                break;
+            case 12:
+                System.arraycopy(GREYSCALE12, 0, greypattern, 0, 32);
+                break;
+            case 13:
+                System.arraycopy(GREYSCALE13, 0, greypattern, 0, 32);
+                break;
+            case 14:
+                System.arraycopy(GREYSCALE14, 0, greypattern, 0, 32);
+                break;
+            case 15:
+                System.arraycopy(GREYSCALE15, 0, greypattern, 0, 32);
+                break;
+            case 16:
+                System.arraycopy(GREYSCALE16, 0, greypattern, 0, 32);
+                break;
+            default :
+                System.arraycopy(GREYSCALE00, 0, greypattern, 0, 32);
+                break;
+        }
+
+        for(int i = 0; i < repeat; i++) {
+
+            System.arraycopy(greypattern, 0, rasterdata, i * 32, 32);
+
+        }
+
+        return rasterdata;
+
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ImageSegment.java b/src/sandbox/org/apache/fop/render/afp/modca/ImageSegment.java
new file mode 100644 (file)
index 0000000..0e5a2f6
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * An Image Segment is represented by a set of self-defining fields, fields
+ * that describe their own contents.  It starts with a Begin Segment, and
+ * ends with an End Segment.
+ *
+ * Between the Begin Segment and End Segment is the image information to
+ * be processed, called the Image Content.
+ *
+ * Only one Image Content can exist within a single IOCA Image Segment.
+ */
+public class ImageSegment extends AbstractAFPObject {
+
+    /**
+     * Default name for the object environment group
+     */
+    private static final String DEFAULT_NAME = "IS01";
+
+    /**
+     * The name of the image segment
+     */
+    private String _name;
+
+    /**
+     * The name of the image segment as EBCIDIC bytes
+     */
+    private byte[] _nameBytes;
+
+    /**
+     * The ImageContent for the image segment
+     */
+    private ImageContent _imageContent = null;
+
+    /**
+     * Default constructor for the ImageSegment.
+     */
+    public ImageSegment() {
+
+        this(DEFAULT_NAME);
+
+    }
+
+    /**
+     * Constructor for the image segment with the specified name,
+     * the name must be a fixed length of eight characters.
+     * @param name     The name of the image.
+     */
+    public ImageSegment(String name) {
+
+        if (name.length() != 4) {
+            String msg = "Image segment name must be 4 characters long " + name;
+            log.error("Constructor:: " + msg);
+            throw new IllegalArgumentException(msg);
+        }
+
+        _name = name;
+
+        try {
+
+            _nameBytes = name.getBytes(AFPConstants.EBCIDIC_ENCODING);
+
+        } catch (UnsupportedEncodingException usee) {
+
+            _nameBytes = name.getBytes();
+            log.warn(
+                "Constructor:: UnsupportedEncodingException translating the name "
+                + name);
+
+        }
+
+    }
+
+    /**
+     * Sets the image size parameters
+     * resolution, hsize and vsize.
+     * @param hresol The horizontal resolution of the image.
+     * @param vresol The vertical resolution of the image.
+     * @param hsize The horizontal size of the image.
+     * @param vsize The vertival size of the image.
+     */
+    public void setImageSize(int hresol, int vresol, int hsize, int vsize) {
+        if (_imageContent == null) {
+            _imageContent = new ImageContent();
+        }
+        _imageContent.setImageSize(hresol, vresol, hsize, vsize);
+    }
+
+    /**
+     * Sets the image encoding.
+     * @param encoding The image encoding.
+     */
+    public void setImageEncoding(byte encoding) {
+        if (_imageContent == null) {
+            _imageContent = new ImageContent();
+        }
+        _imageContent.setImageEncoding(encoding);
+    }
+
+    /**
+     * Sets the image compression.
+     * @param compression The image compression.
+     */
+    public void setImageCompression(byte compression) {
+        if (_imageContent == null) {
+            _imageContent = new ImageContent();
+        }
+        _imageContent.setImageCompression(compression);
+    }
+
+    /**
+     * Sets the image IDE size.
+     * @param size The IDE size.
+     */
+    public void setImageIDESize(byte size) {
+        if (_imageContent == null) {
+            _imageContent = new ImageContent();
+        }
+        _imageContent.setImageIDESize(size);
+    }
+
+    /**
+     * Sets the image IDE color model.
+     * @param size The IDE color model.
+     */
+    public void setImageIDEColorModel(byte colorModel) {
+        if (_imageContent == null) {
+            _imageContent = new ImageContent();
+        }
+        _imageContent.setImageIDEColorModel(colorModel);
+    }
+
+    /**
+     * Set the data of the image.
+     * @param data the image data
+     */
+    public void setImageData(byte data[]) {
+        if (_imageContent == null) {
+            _imageContent = new ImageContent();
+        }
+        _imageContent.setImageData(data);
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Image Segment
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        writeStart(os);
+
+        if (_imageContent != null) {
+            _imageContent.writeDataStream(os);
+        }
+
+        writeEnd(os);
+
+    }
+
+    /**
+     * Helper method to write the start of the Image Segment.
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[] {
+            0x70, // ID
+            0x04, // Length
+            0x00, // Name byte 1
+            0x00, // Name byte 2
+            0x00, // Name byte 3
+            0x00, // Name byte 4
+        };
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[2 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the Image Segment.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[] {
+            0x71, // ID
+            0x00, // Length
+        };
+
+        os.write(data);
+
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ImageSizeParameter.java b/src/sandbox/org/apache/fop/render/afp/modca/ImageSizeParameter.java
new file mode 100644 (file)
index 0000000..46e9318
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * Describes the measurement characteristics of the image when it is created.
+ */
+public class ImageSizeParameter extends AbstractAFPObject {
+
+    private int _hresol = 0;
+    private int _vresol = 0;
+    private int _hsize = 0;
+    private int _vsize = 0;
+
+    /**
+     * Constructor for a ImageSizeParameter for the specified
+     * resolution, hsize and vsize.
+     * @param hresol The horizontal resolution of the image.
+     * @param vresol The vertical resolution of the image.
+     * @param hsize The hsize of the image.
+     * @param vsize The vsize of the vsize.
+     */
+    public ImageSizeParameter(int hresol, int vresol, int hsize, int vsize) {
+
+        _hresol = hresol;
+        _vresol = vresol;
+        _hsize = hsize;
+        _vsize = vsize;
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Image Size Parameter
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[] {
+            (byte)0x94, // ID = Image Size Parameter
+            0x09, // Length
+            0x00, // Unit base - 10 Inches
+            0x00, // HRESOL
+            0x00, //
+            0x00, // VRESOL
+            0x00, //
+            0x00, // HSIZE
+            0x00, //
+            0x00, // VSIZE
+            0x00, //
+        };
+
+        byte[] x = BinaryUtils.convert(_hresol, 2);
+        data[3] = x[0];
+        data[4] = x[1];
+
+        byte[] y = BinaryUtils.convert(_vresol, 2);
+        data[5] = y[0];
+        data[6] = y[1];
+
+        byte[] w = BinaryUtils.convert(_hsize, 2);
+        data[7] = w[0];
+        data[8] = w[1];
+
+        byte[] h = BinaryUtils.convert(_vsize, 2);
+        data[9] = h[0];
+        data[10] = h[1];
+
+        os.write(data);
+
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/IncludeObject.java b/src/sandbox/org/apache/fop/render/afp/modca/IncludeObject.java
new file mode 100644 (file)
index 0000000..257cf1e
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * An Include Object structured field references an object on a page or overlay.
+ * It optionally contains parameters that identify the object and that specify
+ * presentation parameters such as object position, size, orientation, mapping,
+ * and default color.
+ * <p>
+ * Where the presentation parameters conflict with parameters specified in the
+ * object's environment group (OEG), the parameters in the Include Object
+ * structured field override. If the referenced object is a page segment, the
+ * IOB parameters override the corresponding environment group parameters on all
+ * data objects in the page segment.
+ * </p>
+ */
+public class IncludeObject extends AbstractNamedAFPObject {
+
+    /**
+     * The object type
+     */
+    private byte _objectType = (byte) 0x92;
+
+    /**
+     * The orientation on the include object
+     */
+    private int _orientation = 0;
+
+    /**
+     * Constructor for the include object with the specified name, the name must
+     * be a fixed length of eight characters and is the name of the referenced
+     * object.
+     *
+     * @param name
+     *            the name of the image
+     */
+    public IncludeObject(String name) {
+
+        super(name);
+        _objectType = (byte) 0xFB;
+
+    }
+
+    /**
+     * Sets the orienation to use for the Include Object.
+     *
+     * @param orientation
+     *            The orientation (0,90, 180, 270)
+     */
+    public void setOrientation(int orientation) {
+
+        if (orientation == 0 || orientation == 90 || orientation == 180
+            || orientation == 270) {
+            _orientation = orientation;
+        } else {
+            throw new IllegalArgumentException(
+                "The orientation must be one of the values 0, 90, 180, 270");
+        }
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Include Object
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[37];
+
+        data[0] = 0x5A;
+
+        // Set the total record length
+        byte[] rl1 = BinaryUtils.convert(36, 2); //Ignore first byte
+        data[1] = rl1[0];
+        data[2] = rl1[1];
+
+        // Structured field ID for a IOB
+        data[3] = (byte) 0xD3;
+        data[4] = (byte) 0xAF;
+        data[5] = (byte) 0xC3;
+
+        data[6] = 0x00; // Reserved
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+            data[9 + i] = _nameBytes[i];
+        }
+
+        data[17] = 0x00;
+        data[18] = _objectType;
+
+        // XoaOset
+        data[20] = (byte) 0xFF;
+        data[21] = (byte) 0xFF;
+        data[22] = (byte) 0xFF;
+
+        // YoaOset
+        data[23] = (byte) 0xFF;
+        data[24] = (byte) 0xFF;
+        data[25] = (byte) 0xFF;
+
+        switch (_orientation) {
+            case 90:
+                data[26] = 0x2D;
+                data[27] = 0x00;
+                data[28] = 0x5A;
+                data[29] = 0x00;
+                break;
+            case 180:
+                data[26] = 0x5A;
+                data[27] = 0x00;
+                data[28] = (byte) 0x87;
+                data[29] = 0x00;
+                break;
+            case 270:
+                data[26] = (byte) 0x87;
+                data[27] = 0x00;
+                data[28] = 0x00;
+                data[29] = 0x00;
+                break;
+            default:
+                data[26] = 0x00;
+                data[27] = 0x00;
+                data[28] = 0x2D;
+                data[29] = 0x00;
+                break;
+        }
+
+        // XocaOset
+        data[30] = (byte) 0xFF;
+        data[31] = (byte) 0xFF;
+        data[32] = (byte) 0xFF;
+
+        // YocaOset
+        data[33] = (byte) 0xFF;
+        data[34] = (byte) 0xFF;
+        data[35] = (byte) 0xFF;
+
+        data[36] = 0x01;
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/IncludePageOverlay.java b/src/sandbox/org/apache/fop/render/afp/modca/IncludePageOverlay.java
new file mode 100644 (file)
index 0000000..4fb8b9c
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ *
+ * The Include Page Overlay structured field references an overlay resource
+ * definition that is to be positioned on the page. A page overlay can be
+ * referenced at any time during the page state, but not during an object state.
+ * The overlay contains its own active environment group definition.
+ *
+ * Note: There is no need for the triplets, so I have ignored them.
+ *
+ * A real example of where this will be used is for static overlays, such as an
+ * address on the page.
+ *
+ */
+public class IncludePageOverlay extends AbstractNamedAFPObject {
+
+    /**
+     * The x coordinate
+     */
+    private int _xCoor = 0;
+
+    /**
+     * The y coordinate
+     */
+    private int _yCoor = 0;
+
+    /**
+     * The orientation
+     */
+    private int _orientation = 0;
+
+    /**
+     * Constructor for the Include Page Overlay
+     * @param overlayName Name of the page segment
+     * @param x The x position
+     * @param y The y position
+     * @param orientation The orientation
+     */
+    public IncludePageOverlay(String overlayName, int x, int y, int orientation) {
+
+        super(overlayName);
+
+        _xCoor = x;
+        _yCoor = y;
+        setOrientation(orientation);
+    }
+
+    /**
+     * Sets the orienation to use for the overlay.
+     *
+     * @param orientation
+     *            The orientation (0,90, 180, 270)
+     */
+    public void setOrientation(int orientation) {
+
+        if (orientation == 0 || orientation == 90 || orientation == 180
+            || orientation == 270) {
+            _orientation = orientation;
+        } else {
+            throw new IllegalArgumentException(
+                "The orientation must be one of the values 0, 90, 180, 270");
+        }
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Include Page Overlay
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[25]; //(9 +16)
+
+        data[0] = 0x5A;
+
+        // Set the total record length
+        byte[] rl1 = BinaryUtils.convert(24, 2); //Ignore first byte
+        data[1] = rl1[0];
+        data[2] = rl1[1];
+
+        // Structured field ID for a IPO
+        data[3] = (byte) 0xD3;
+        data[4] = (byte) 0xAF;
+        data[5] = (byte) 0xD8;
+
+        data[6] = 0x00; // Reserved
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        byte[] r2 = BinaryUtils.convert(_xCoor, 3);
+        data[17] = r2[0]; // x coordinate
+        data[18] = r2[1];
+        data[19] = r2[2];
+
+        byte[] r3 = BinaryUtils.convert(_yCoor, 3);
+        data[20] = r3[0]; // y coordinate
+        data[21] = r3[1];
+        data[22] = r3[2];
+
+        switch (_orientation) {
+            case 90:
+                data[23] = 0x2D;
+                data[24] = 0x00;
+                break;
+            case 180:
+                data[23] = 0x5A;
+                data[24] = 0x00;
+                break;
+            case 270:
+                data[23] = (byte) 0x87;
+                data[24] = 0x00;
+                break;
+            default:
+                data[23] = 0x00;
+                data[24] = 0x00;
+                break;
+        }
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/IncludePageSegment.java b/src/sandbox/org/apache/fop/render/afp/modca/IncludePageSegment.java
new file mode 100644 (file)
index 0000000..5db736d
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * The Include Page Segment structured field references a page segment resource
+ * object that is to be presented on the page or overlay presentation space. The IPS
+ * specifies a reference point on the including page or overlay coordinate system that
+ * may be used to position objects contained in the page segment. A page segment
+ * can be referenced at any time during page or overlay state, but not during an
+ * object state. The page segment inherits the active environment group definition of
+ * the including page or overlay.
+ *
+ * Note : No use for Triplets.
+ *
+ * A 'real' example for where this will be used is for
+ * the dynamic placing of overlay objects, such as signatures
+ * that may have to be placed at different positions on a document.
+ *
+ */
+public class IncludePageSegment extends AbstractNamedAFPObject{
+
+    /**
+     * The x position where we need to put this object on the page
+     */
+    private byte [] _xCoor;
+
+    /**
+     * The y position where we need to put this object on the page
+     */
+    private byte [] _yCoor;
+
+    /**
+     * Constructor for the Include Page Segment
+     * @param name Name of the page segment
+     * @param xVal The x position
+     * @param yVal The y position
+     */
+    public IncludePageSegment(String name, int xVal, int yVal){
+
+        super(name);
+        _xCoor = BinaryUtils.convert(xVal, 3);
+        _yCoor = BinaryUtils.convert(yVal, 3);
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Include Page Segment
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[23]; //(9 +14)
+
+        data[0] = 0x5A;
+
+        // Set the total record length
+        byte[] rl1 = BinaryUtils.convert(22, 2); //Ignore first byte
+        data[1] = rl1[0];
+        data[2] = rl1[1];
+
+        // Structured field ID for a IPS
+        data[3] = (byte) 0xD3;
+        data[4] = (byte) 0xAF;
+        data[5] = (byte) 0x5F;
+
+        data[6] = 0x00; // Reserved
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        data[17] = _xCoor[0]; // x coordinate
+        data[18] = _xCoor[1];
+        data[19] = _xCoor[2];
+
+        data[20] = _yCoor[0]; // y coordinate
+        data[21] = _yCoor[1];
+        data[22] = _yCoor[2];
+
+        os.write(data);
+
+    }
+
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/InvokeMediumMap.java b/src/sandbox/org/apache/fop/render/afp/modca/InvokeMediumMap.java
new file mode 100644 (file)
index 0000000..14570ec
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * The Invoke Medium Map structured field identifies the Medium Map that is to
+ * become active for the document. An Invoke Medium Map structured field affects
+ * the document's current environment. The Medium Map's effect on current environment
+ * parameter values lasts until a new Medium Map is invoked.
+ */
+public class InvokeMediumMap extends AbstractNamedAFPObject {
+
+    /**
+     * Constructor for the Invoke Medium Map
+     * @param mediumMapName Name of the medium map
+     */
+    public InvokeMediumMap(String mediumMapName) {
+
+        super(mediumMapName);
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Invoke Medium Map
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A;
+
+        // Set the total record length
+        byte[] rl1 = BinaryUtils.convert(16, 2); //Ignore first byte
+        data[1] = rl1[0];
+        data[2] = rl1[1];
+
+        // Structured field ID for a IPO
+        data[3] = (byte) 0xD3;
+        data[4] = (byte) 0xAB;
+        data[5] = (byte) 0xCC;
+
+        data[6] = 0x00; // Reserved
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/MapCodedFont.java b/src/sandbox/org/apache/fop/render/afp/modca/MapCodedFont.java
new file mode 100644 (file)
index 0000000..9290dfc
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+
+import org.apache.fop.render.afp.exceptions.FontRuntimeException;
+import org.apache.fop.render.afp.fonts.AFPFont;
+import org.apache.fop.render.afp.fonts.CharacterSet;
+import org.apache.fop.render.afp.fonts.OutlineFont;
+import org.apache.fop.render.afp.fonts.RasterFont;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * The Map Coded Font structured field maps a unique coded font resource local
+ * ID, which may be embedded one or more times within an object's data and
+ * descriptor, to the identifier of a coded font resource object. Additionally,
+ * the Map Coded Font structured field specifies a set of resource attributes
+ * for the coded font.
+ *
+ * @author <a href="mailto:pete@townsend.uk.com">Pete Townsend </a>
+ */
+public class MapCodedFont extends AbstractAFPObject {
+
+    /**
+     * The collection of map coded fonts (maximum of 254)
+     */
+    private ArrayList _fontlist = null;
+
+    /**
+     * Constructor for the MapCodedFont
+     */
+    public MapCodedFont() {
+
+        _fontlist = new ArrayList();
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Map Coded Font
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        baos.write(0x5A);
+        baos.write(new byte[] { 0x00, 0x00 });
+
+        // Field identifier for a MapCodedFont
+        baos.write(new byte[] { (byte) 0xD3, (byte) 0xAB, (byte) 0x8A });
+
+        // Reserved
+        baos.write(new byte[] { 0x00, 0x00, 0x00 });
+
+        for (int i = 0; i < _fontlist.size(); i++) {
+
+            FontDefinition fd = (FontDefinition) _fontlist.get(i);
+
+            // Start of repeating groups (occurs 1 to 254)
+            baos.write(0x00);
+
+            if (fd._scale == 0) {
+                // Raster Font
+                baos.write(0x22); // Length of 34
+            } else {
+                // Outline Font
+                baos.write(0x3A); // Length of 58
+            }
+
+            // Font Character Set Name Reference
+            baos.write(0x0C);
+            baos.write(0x02);
+            baos.write((byte) 0x86);
+            baos.write(0x00);
+            baos.write(fd._characterset);
+
+            // Font Code Page Name Reference
+            baos.write(0x0C);
+            baos.write(0x02);
+            baos.write((byte) 0x85);
+            baos.write(0x00);
+            baos.write(fd._codepage);
+
+            // Character Rotation
+            baos.write(0x04);
+            baos.write(0x26);
+            baos.write(fd._orientation);
+            baos.write(0x00);
+
+            // Resource Local Identifier
+            baos.write(0x04);
+            baos.write(0x24);
+            baos.write(0x05);
+            baos.write(fd._fontReferenceKey);
+
+            if (fd._scale != 0) {
+                // Outline Font (triplet '1F')
+                baos.write(0x14);
+                baos.write(0x1F);
+                baos.write(0x00);
+                baos.write(0x00);
+
+                baos.write(BinaryUtils.convert(fd._scale, 2)); // Height
+                baos.write(new byte[] { 0x00, 0x00 }); // Width
+
+                baos.write(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00 });
+
+                baos.write(0x60);
+
+                // Outline Font (triplet '5D')
+                baos.write(0x04);
+                baos.write(0x5D);
+                baos.write(BinaryUtils.convert(fd._scale, 2));
+            }
+
+        }
+
+        byte[] data = baos.toByteArray();
+
+        // Set the total record length
+        byte[] rl1 = BinaryUtils.convert(data.length - 1, 2);
+        data[1] = rl1[0];
+        data[2] = rl1[1];
+
+        os.write(data);
+
+    }
+
+    /**
+     * Add a font definition on the the map coded font object.
+     *
+     * @param fontReference
+     *            the font number used as the resource identifier
+     * @param font
+     *            the font
+     * @param size
+     *            the size of the font
+     * @param orientation
+     *            the orientation of the font
+     */
+    public void addFont(byte fontReference, AFPFont font, int size, int orientation)
+        throws MaximumSizeExceededException {
+
+        FontDefinition fd = new FontDefinition();
+
+        fd._fontReferenceKey = fontReference;
+
+        switch (orientation) {
+            case 90:
+                fd._orientation = 0x2D;
+                break;
+            case 180:
+                fd._orientation = 0x5A;
+                break;
+            case 270:
+                fd._orientation = (byte) 0x87;
+                break;
+            default:
+                fd._orientation = 0x00;
+                break;
+        }
+
+        try {
+
+            if (font instanceof RasterFont) {
+
+                RasterFont raster = (RasterFont) font;
+                CharacterSet cs = raster.getCharacterSet(size);
+                if (cs == null) {
+                    String msg = "Character set not found for font "
+                        + font.getFontName() + " with point size " + size;
+                    log.error(msg);
+                    throw new FontRuntimeException(msg);
+                }
+
+                fd._characterset = cs.getNameBytes();
+
+                if (fd._characterset.length != 8) {
+                    throw new IllegalArgumentException("The character set "
+                        + new String(fd._characterset,
+                        AFPConstants.EBCIDIC_ENCODING)
+                        + " must have a fixed length of 8 characters.");
+                }
+
+                fd._codepage = cs.getCodePage().getBytes(
+                    AFPConstants.EBCIDIC_ENCODING);
+
+                if (fd._codepage.length != 8) {
+                    throw new IllegalArgumentException("The code page "
+                        + new String(fd._codepage,
+                        AFPConstants.EBCIDIC_ENCODING)
+                        + " must have a fixed length of 8 characters.");
+                }
+
+            } else if (font instanceof OutlineFont) {
+
+                OutlineFont outline = (OutlineFont) font;
+                CharacterSet cs = outline.getCharacterSet();
+                fd._characterset = cs.getNameBytes();
+
+                // There are approximately 72 points to 1 inch or 20 1440ths per point.
+
+                fd._scale = ((size / 1000) * 20);
+
+                fd._codepage = cs.getCodePage().getBytes(
+                    AFPConstants.EBCIDIC_ENCODING);
+
+                if (fd._codepage.length != 8) {
+                    throw new IllegalArgumentException("The code page "
+                        + new String(fd._codepage,
+                        AFPConstants.EBCIDIC_ENCODING)
+                        + " must have a fixed length of 8 characters.");
+                }
+
+            } else {
+                String msg = "Font of type " + font.getClass().getName()
+                    + " not recognized.";
+                log.error(msg);
+                throw new FontRuntimeException(msg);
+            }
+
+            if (_fontlist.size() > 253) {
+
+                // Throw an exception if the size is exceeded
+                throw new MaximumSizeExceededException();
+
+            } else {
+
+                _fontlist.add(fd);
+
+            }
+
+        } catch (UnsupportedEncodingException ex) {
+
+            throw new FontRuntimeException("Failed to create font "
+                + " due to a UnsupportedEncodingException", ex);
+
+        }
+
+    }
+
+    /**
+     * Private utility class used as a container for font attributes
+     */
+    private class FontDefinition {
+
+        /**
+         * The code page of the font
+         */
+        private byte[] _codepage;
+
+        /**
+         * The character set of the font
+         */
+        private byte[] _characterset;
+
+        /**
+         * The font reference key
+         */
+        private byte _fontReferenceKey;
+
+        /**
+         * The orientation of the font
+         */
+        private byte _orientation;
+
+        /**
+         * The scale (only specified for outline fonts)
+         */
+        private int _scale = 0;
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/MapPageOverlay.java b/src/sandbox/org/apache/fop/render/afp/modca/MapPageOverlay.java
new file mode 100644 (file)
index 0000000..b2821b6
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * The Map Page Overlay structured field maps a Resource Local ID to the name of
+ * a Begin Overlay structured field. A Map Page Overlay structured field may
+ * contain from one to 254 repeating groups.
+ *
+ */
+public class MapPageOverlay extends AbstractAFPObject {
+
+    /**
+     * The collection of overlays (maximum of 254 stored as byte[])
+     */
+    private ArrayList _overLays = new ArrayList();
+
+    /**
+     * Constructor for the Map Page Overlay
+     */
+    public MapPageOverlay() {
+
+    }
+
+    /**
+     * Add an overlay to to the map page overlay object.
+     *
+     * @param name
+     *            The name of the overlay.
+     */
+    public void addOverlay(String name) throws MaximumSizeExceededException {
+
+        if (_overLays.size() > 253) {
+            throw new MaximumSizeExceededException();
+        }
+
+        if (name.length() != 8) {
+            throw new IllegalArgumentException("The name of overlay " + name
+                + " must be 8 characters");
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("addOverlay():: adding overlay " + name);
+        }
+
+        byte[] data;
+
+        try {
+
+            data = name.getBytes(AFPConstants.EBCIDIC_ENCODING);
+            _overLays.add(data);
+
+        } catch (UnsupportedEncodingException usee) {
+
+            log
+                .error("addOverlay():: UnsupportedEncodingException translating the name "
+                + name);
+
+        }
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Map Page Overlay
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+
+        int oLayCount = _overLays.size();
+        int recordlength = oLayCount * 18;
+
+        byte[] data = new byte[recordlength + 9];
+
+        data[0] = 0x5A;
+
+        // Set the total record length
+        byte[] rl1 = BinaryUtils.convert(recordlength + 8, 2); //Ignore the
+        // first byte in
+        // the length
+        data[1] = rl1[0];
+        data[2] = rl1[1];
+
+        // Structured field ID for a MPO
+        data[3] = (byte) 0xD3;
+        data[4] = (byte) 0xAB;
+        data[5] = (byte) 0xD8;
+
+        data[6] = 0x00; // Reserved
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        int pos = 8;
+
+        //For each overlay
+        byte olayref = 0x00;
+
+        for (int i = 0; i < oLayCount; i++) {
+
+            olayref = (byte) (olayref + 1);
+
+            data[++pos] = 0x00;
+            data[++pos] = 0x12; //the length of repeating group
+
+            data[++pos] = 0x0C; //Fully Qualified Name
+            data[++pos] = 0x02;
+            data[++pos] = (byte) 0x84;
+            data[++pos] = 0x00;
+
+            //now add the name
+            byte[] name = (byte[]) _overLays.get(i);
+
+            for (int j = 0; j < name.length; j++) {
+
+                data[++pos] = name[j];
+
+            }
+
+            data[++pos] = 0x04; //Resource Local Identifier (RLI)
+            data[++pos] = 0x24;
+            data[++pos] = 0x02;
+
+            //now add the unique id to the RLI
+            data[++pos] = olayref;
+
+        }
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/MaximumSizeExceededException.java b/src/sandbox/org/apache/fop/render/afp/modca/MaximumSizeExceededException.java
new file mode 100644 (file)
index 0000000..add47a0
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+/**
+ * An exception to handle maximum sizes being exceeded.
+ *
+ */
+public class MaximumSizeExceededException extends Exception {
+
+    public MaximumSizeExceededException() {
+        super();
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ObjectAreaDescriptor.java b/src/sandbox/org/apache/fop/render/afp/modca/ObjectAreaDescriptor.java
new file mode 100644 (file)
index 0000000..fbefef0
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * The Object Area Descriptor structured field specifies the size and attributes
+ * of an object area presentation space.
+ *
+ */
+public class ObjectAreaDescriptor extends AbstractAFPObject {
+
+    private int _width = 0;
+    private int _height = 0;
+
+    /**
+     * Construct an object area descriptor for the specified object width
+     * and object height.
+     * @param width The page width.
+     * @param height The page height.
+     */
+    public ObjectAreaDescriptor(int width, int height) {
+
+        _width = width;
+        _height = height;
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Object Area Descriptor
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[] {
+            0x5A,
+            0x00, // Length
+            0x1C, // Length
+            (byte) 0xD3,
+            (byte) 0xA6,
+            (byte) 0x6B,
+            0x00, // Flags
+            0x00, // Reserved
+            0x00, // Reserved
+            0x03, // Triplet length
+            0x43, // tid = Descriptor Position Triplet
+            0x01, // DesPosId = 1
+            0x08, // Triplet length
+            0x4B, // tid = Measurement Units Triplet
+            0x00, // XaoBase = 10 inches
+            0x00, // YaoBase = 10 inches
+            0x09, // XaoUnits = 2400
+            0x60, // XaoUnits =
+            0x09, // YaoUnits = 2400
+            0x60, // YaoUnits =
+            0x09, // Triplet length
+            0x4C, // tid = Object Area Size
+            0x02, // Size Type
+            0x00, // XoaSize
+            0x00,
+            0x00,
+            0x00, // YoaSize
+            0x00,
+            0x00,
+        };
+
+        byte[] l = BinaryUtils.convert(data.length - 1, 2);
+        data[1] = l[0];
+        data[2] = l[1];
+
+        byte[] x = BinaryUtils.convert(_width, 3);
+        data[23] = x[0];
+        data[24] = x[1];
+        data[25] = x[2];
+
+        byte[] y = BinaryUtils.convert(_height, 3);
+        data[26] = y[0];
+        data[27] = y[1];
+        data[28] = y[2];
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ObjectAreaPosition.java b/src/sandbox/org/apache/fop/render/afp/modca/ObjectAreaPosition.java
new file mode 100644 (file)
index 0000000..7089a41
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * The Object Area Position structured field specifies the origin and
+ * orientation of the object area, and the origin and orientation of the
+ * object content within the object area.
+ */
+public class ObjectAreaPosition extends AbstractAFPObject {
+
+    private int _x = 0;
+    private int _y = 0;
+    private int _rot = 0;
+
+    /**
+     * Construct an object area position for the specified object y, y position.
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     * @param rotation The coordinate system rotation (must be 0, 90, 180, 270).
+     */
+    public ObjectAreaPosition(int x, int y, int rotation) {
+
+        _x = x;
+        _y = y;
+        _rot = rotation;
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Object Area Position
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[] {
+            0x5A,
+            0x00, // Length
+            0x20, // Length
+            (byte) 0xD3,
+            (byte) 0xAC,
+            (byte) 0x6B,
+            0x00, // Flags
+            0x00, // Reserved
+            0x00, // Reserved
+            0x01, // OAPosID = 1
+            0x17, // RGLength = 23
+            0x00, // XoaOSet
+            0x00,
+            0x00,
+            0x00, // YoaOSet
+            0x00,
+            0x00,
+            (byte)(_rot / 2), // XoaOrent
+            0x00,
+            (byte)(_rot / 2 + 45), // YoaOrent
+            0x00,
+            0x00, // Reserved
+            0x00, // XocaOSet
+            0x00,
+            0x00,
+            0x00, // YocaOSet
+            0x00,
+            0x00,
+            0x00, // XocaOrent
+            0x00,
+            0x2D, // YocaOrent
+            0x00,
+            0x01, // RefCSys
+        };
+
+        byte[] l = BinaryUtils.convert(data.length - 1, 2);
+        data[1] = l[0];
+        data[2] = l[1];
+
+        byte[] x = BinaryUtils.convert(_x, 3);
+        data[11] = x[0];
+        data[12] = x[1];
+        data[13] = x[2];
+
+        byte[] y = BinaryUtils.convert(_y, 3);
+        data[14] = y[0];
+        data[15] = y[1];
+        data[16] = y[2];
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ObjectEnvironmentGroup.java b/src/sandbox/org/apache/fop/render/afp/modca/ObjectEnvironmentGroup.java
new file mode 100644 (file)
index 0000000..91d697e
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+
+/**
+ * An Object Environment Group (OEG) may be associated with an object and is contained
+ * within the object's begin-end envelope.
+ * The object environment group defines the object's origin and orientation on the page,
+ * and can contain font and color attribute table information. The scope of an object
+ * environment group is the scope of its containing object.
+ *
+ * An application that creates a data-stream document may omit some of the parameters
+ * normally contained in the object environment group, or it may specify that one or
+ * more default values are to be used.
+ */
+public final class ObjectEnvironmentGroup extends AbstractNamedAFPObject {
+
+    /**
+     * Default name for the object environment group
+     */
+    private static final String DEFAULT_NAME = "OEG00001";
+
+    /**
+     * The ObjectAreaDescriptor for the object environment group
+     */
+    private ObjectAreaDescriptor _objectAreaDescriptor = null;
+
+    /**
+     * The ObjectAreaPosition for the object environment group
+     */
+    private ObjectAreaPosition _objectAreaPosition = null;
+
+    /**
+     * The ImageDataDescriptor for the object environment group
+     */
+    private ImageDataDescriptor _imageDataDescriptor = null;
+
+    /**
+     * Default constructor for the ObjectEnvironmentGroup.
+     */
+    public ObjectEnvironmentGroup() {
+
+        this(DEFAULT_NAME);
+
+    }
+
+    /**
+     * Constructor for the ObjectEnvironmentGroup, this takes a
+     * name parameter which must be 8 characters long.
+     * @param name the object environment group name
+     */
+    public ObjectEnvironmentGroup(String name) {
+
+        super(name);
+
+    }
+
+    /**
+     * Sets the object area parameters.
+     * @param x the x position of the object
+     * @param y the y position of the object
+     * @param width the object width
+     * @param height the object height
+     * @param rotation the object orientation
+     */
+    public void setObjectArea(int x, int y, int width, int height, int rotation) {
+
+        _objectAreaDescriptor = new ObjectAreaDescriptor(width, height);
+        _objectAreaPosition = new ObjectAreaPosition(x, y, rotation);
+
+    }
+
+    /**
+     * Set the dimensions of the image.
+     * @param xresol the x resolution of the image
+     * @param yresol the y resolution of the image
+     * @param width the image width
+     * @param height the image height
+     */
+    public void setImageData(int xresol, int yresol, int width, int height) {
+        _imageDataDescriptor = new ImageDataDescriptor(xresol, yresol,  width, height);
+    }
+
+    /**
+     * Accessor method to obtain write the AFP datastream for
+     * the object environment group.
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+
+        writeStart(os);
+
+        _objectAreaDescriptor.writeDataStream(os);
+
+        _objectAreaPosition.writeDataStream(os);
+
+        if (_imageDataDescriptor != null) {
+            _imageDataDescriptor.writeDataStream(os);
+        }
+
+        writeEnd(os);
+
+    }
+
+    /**
+     * Helper method to write the start of the object environment group.
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[] {
+            0x5A, // Structured field identifier
+            0x00, // Length byte 1
+            0x10, // Length byte 2
+            (byte) 0xD3, // Structured field id byte 1
+            (byte) 0xA8, // Structured field id byte 2
+            (byte) 0xC7, // Structured field id byte 3
+            0x00, // Flags
+            0x00, // Reserved
+            0x00, // Reserved
+            0x00, // Name
+            0x00, //
+            0x00, //
+            0x00, //
+            0x00, //
+            0x00, //
+            0x00, //
+            0x00, //
+        };
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the object environment group.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA9; // Structured field id byte 2
+        data[5] = (byte) 0xC7; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/Overlay.java b/src/sandbox/org/apache/fop/render/afp/modca/Overlay.java
new file mode 100644 (file)
index 0000000..85d8b2a
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Iterator;
+
+/**
+ */
+public class Overlay extends AbstractPageObject{
+
+    /**
+     * Construct a new overlay object for the specified name argument, the overlay
+     * name should be an 8 character identifier.
+     *
+     * @param name
+     *            the name of the page.
+     * @param width
+     *            the width of the page.
+     * @param height
+     *            the height of the page.
+     * @param rotation
+     *            the rotation of the page.
+     */
+    public Overlay(String name, int width, int height, int rotation) {
+
+        super(name, width, height, rotation);
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the overlay.
+     *
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        writeStart(os);
+
+        _activeEnvironmentGroup.writeDataStream(os);
+
+        writeObjectList(_segments, os);
+
+        writeObjectList(_tagLogicalElements, os);
+
+        writeObjectList(_objects, os);
+
+        writeEnd(os);
+
+    }
+
+    /**
+     * Helper method to write the start of the overlay.
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA8; // Structured field id byte 2
+        data[5] = (byte) 0xDF; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the overlay.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA9; // Structured field id byte 2
+        data[5] = (byte) 0xDF; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/PageDescriptor.java b/src/sandbox/org/apache/fop/render/afp/modca/PageDescriptor.java
new file mode 100644 (file)
index 0000000..3132f17
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * The Page Descriptor structured field specifies the size and attributes of
+ * a page or overlay presentation space.
+ *
+ */
+public class PageDescriptor extends AbstractAFPObject {
+
+    private int _width = 0;
+    private int _height = 0;
+
+    /**
+     * Construct a page descriptor for the specified page width
+     * and page height.
+     * @param width The page width.
+     * @param height The page height.
+     */
+    public PageDescriptor(int width, int height) {
+
+        _width = width;
+        _height = height;
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Page Descriptor
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[] {
+            0x5A,
+            0x00,
+            0x17,
+            (byte) 0xD3,
+            (byte) 0xA6,
+            (byte) 0xAF,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x09,
+            0x60,
+            0x09,
+            0x60,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+        };
+
+        byte[] x = BinaryUtils.convert(_width, 3);
+        data[15] = x[0];
+        data[16] = x[1];
+        data[17] = x[2];
+
+        byte[] y = BinaryUtils.convert(_height, 3);
+        data[18] = y[0];
+        data[19] = y[1];
+        data[20] = y[2];
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/PageGroup.java b/src/sandbox/org/apache/fop/render/afp/modca/PageGroup.java
new file mode 100644 (file)
index 0000000..ea78d76
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A page group is used in the data stream to define a named, logical grouping
+ * of sequential pages. Page groups are delimited by begin-end structured fields
+ * that carry the name of the page group. Page groups are defined so that the
+ * pages that comprise the group can be referenced or processed as a single
+ * entity. Page groups are often processed in stand-alone fashion; that is, they
+ * are indexed, retrieved, and presented outside the context of the containing
+ * document.
+ *
+ * @author <a href="mailto:pete@townsend.uk.com">Pete Townsend </a>
+ */
+public class PageGroup extends AbstractNamedAFPObject {
+
+    /**
+     * The pages contained within this group
+     */
+    private List _objects = new ArrayList();
+
+    /**
+     * The tag logical elements contained within this group
+     */
+    private List _tagLogicalElements = new ArrayList();
+
+    /**
+     * The page state
+     */
+    private boolean _complete = false;
+
+    /**
+     * Constructor for the PageGroup.
+     *
+     * @param name
+     *            the name of the page group
+     */
+    public PageGroup(String name) {
+
+        super(name);
+
+    }
+
+    /**
+     * Adds a page object to the group.
+     *
+     * @param page
+     *            the page object to add
+     */
+    public void addPage(PageObject page) {
+
+        if (!_objects.contains(page)) {
+            _objects.add(page);
+        }
+
+    }
+
+    /**
+     * @return the name of the page group
+     */
+    public String getName() {
+        return _name;
+    }
+
+    /**
+     * Creates a TagLogicalElement on the page.
+     *
+     * @param name
+     *            the name of the tag
+     * @param value
+     *            the value of the tag
+     */
+    public void createTagLogicalElement(String name, String value) {
+
+        TagLogicalElement tle = new TagLogicalElement(name, value);
+        _tagLogicalElements.add(tle);
+
+    }
+
+    /**
+     * Creates an InvokeMediaMap on the page.
+     *
+     * @param name
+     *            the name of the media map
+     */
+    public void createInvokeMediumMap(String name) {
+
+        InvokeMediumMap imm = new InvokeMediumMap(name);
+        _objects.add(imm);
+
+    }
+
+    /**
+     * Method to mark the end of the page group.
+     */
+    public void endPageGroup() {
+
+        _complete = true;
+
+    }
+
+    /**
+     * Returns an indication if the page group is complete
+     */
+    public boolean isComplete() {
+        return _complete;
+    }
+
+   /**
+     * Accessor method to write the AFP datastream for the page group.
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        writeStart(os);
+
+        writeObjectList(_tagLogicalElements, os);
+
+        writeObjectList(_objects, os);
+
+        writeEnd(os);
+
+    }
+
+    /**
+     * Helper method to write the start of the page group.
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA8; // Structured field id byte 2
+        data[5] = (byte) 0xAD; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the page group.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA9; // Structured field id byte 2
+        data[5] = (byte) 0xAD; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/PageObject.java b/src/sandbox/org/apache/fop/render/afp/modca/PageObject.java
new file mode 100644 (file)
index 0000000..9bf543e
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+
+/**
+ * Pages contain the data objects that comprise a presentation document. Each
+ * page has a set of data objects associated with it. Each page within a
+ * document is independent from any other page, and each must establish its own
+ * environment parameters.
+ *
+ * The page is the level in the document component hierarchy that is used for
+ * printing or displaying a document's content. The data objects contained in
+ * the page envelope in the data stream are presented when the page is
+ * presented. Each data object has layout information associated with it that
+ * directs the placement and orientation of the data on the page. In addition,
+ * each page contains layout information that specifies the measurement units,
+ * page width, and page depth.
+ *
+ * A page is initiated by a begin page structured field and terminated by an end
+ * page structured field. Structured fields that define objects and active
+ * environment groups or that specify attributes of the page may be encountered
+ * in page state.
+ *
+ */
+public class PageObject extends AbstractPageObject {
+
+    /**
+     * The resource group object
+     */
+    private ResourceGroup _resourceGroup = null;
+
+    /**
+     * Construct a new page object for the specified name argument, the page
+     * name should be an 8 character identifier.
+     *
+     * @param name
+     *            the name of the page.
+     * @param width
+     *            the width of the page.
+     * @param height
+     *            the height of the page.
+     * @param rotation
+     *            the rotation of the page.
+     */
+    public PageObject(String name, int width, int height, int rotation) {
+
+        super(name, width, height, rotation);
+
+    }
+
+    /**
+     * Adds an overlay to the page resources
+     * @param the overlay to add
+     */
+    public void addOverlay(Overlay overlay) {
+        if (_resourceGroup == null) {
+            _resourceGroup = new ResourceGroup();
+        }
+        _resourceGroup.addOverlay(overlay);
+    }
+
+    /**
+     * Creates an IncludePageOverlay on the page.
+     *
+     * @param name
+     *            the name of the overlay
+     * @param x
+     *            the x position of the overlay
+     * @param y
+     *            the y position of the overlay
+     * @param orientation
+     *            the orientation required for the overlay
+     */
+    public void createIncludePageOverlay(String name, int x, int y, int orientation) {
+
+        IncludePageOverlay ipo = new IncludePageOverlay(name, x, y, orientation);
+        _objects.add(ipo);
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the page.
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        writeStart(os);
+
+        if (_resourceGroup != null) {
+            _resourceGroup.writeDataStream(os);
+        }
+
+        _activeEnvironmentGroup.writeDataStream(os);
+
+        writeObjectList(_segments, os);
+
+        writeObjectList(_tagLogicalElements, os);
+
+        writeObjectList(_objects, os);
+
+        writeEnd(os);
+
+    }
+
+    /**
+     * Helper method to write the start of the page.
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA8; // Structured field id byte 2
+        data[5] = (byte) 0xAF; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the page.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA9; // Structured field id byte 2
+        data[5] = (byte) 0xAF; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/PresentationTextData.java b/src/sandbox/org/apache/fop/render/afp/modca/PresentationTextData.java
new file mode 100644 (file)
index 0000000..38495c8
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.AFPFontColor;
+
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * Presentation text data contains the graphic characters and the control
+ * sequences necessary to position the characters within the object space. The
+ * data consists of: - graphic characters to be presented - control sequences
+ * that position them - modal control sequences that adjust the positions by
+ * small amounts - other functions causing text to be presented with differences
+ * in appearance.
+ *
+ * The graphic characters are expected to conform to a coded font representation
+ * so that they can be translated from the code point in the object data to the
+ * character in the coded font. The units of measure for linear displacements
+ * are derived from the PresentationTextDescriptor or from the hierarchical
+ * defaults.
+ *
+ * In addition to graphic character code points, Presentation Text data can
+ * contain embedded control sequences. These are strings of two or more bytes
+ * which signal an alternate mode of processing for the content of the current
+ * Presentation Text data.
+ *
+ */
+public class PresentationTextData extends AbstractAFPObject {
+
+    /**
+     * The maximum size of the presentation text data.
+     */
+    private static final int MAX_SIZE = 8192;
+
+    /**
+     * The afp data relating to this presentaion text data.
+     */
+    private ByteArrayOutputStream _baos = new ByteArrayOutputStream(1024);
+
+    /**
+     * The current x coordinate.
+     */
+    private int _currentXCoordinate = -1;
+
+    /**
+     * The current y cooridnate
+     */
+    private int _currentYCoordinate = -1;
+
+    /**
+     * The current font
+     */
+    private String _currentFont = "";
+
+    /**
+     * The current orientation
+     */
+    private int _currentOrientation = 0;
+
+    /**
+     * The current color
+     */
+    private AFPFontColor _currentColor = new AFPFontColor(0, 0, 0);
+
+    /**
+     * The current variable space increment
+     */
+    private int _currentVariableSpaceCharacterIncrement = 0;
+
+    /**
+     * The current inter character adjustment
+     */
+    private int _currentInterCharacterAdjustment = 0;
+
+    /**
+     * Default constructor for the PresentationTextData.
+     */
+    public PresentationTextData() {
+
+        this(false);
+
+    }
+
+    /**
+     * Constructor for the PresentationTextData, the boolean flag indicate
+     * whether the control sequence prefix should be set to indicate the start
+     * of a new control sequence.
+     *
+     * @param controlInd
+     *            The control sequence indicator.
+     */
+    public PresentationTextData(boolean controlInd) {
+
+        _baos.write(new byte[] { 0x5A, // Structured field identifier
+            0x00, // Record length byte 1
+            0x00, // Record length byte 2
+            (byte) 0xD3, // PresentationTextData identifier byte 1
+            (byte) 0xEE, // PresentationTextData identifier byte 2
+            (byte) 0x9B, // PresentationTextData identifier byte 3
+            0x00, // Flag
+            0x00, // Reserved
+            0x00, // Reserved
+        }, 0, 9);
+
+        if (controlInd) {
+            _baos.write(new byte[] { 0x2B, (byte) 0xD3 }, 0, 2);
+        }
+
+    }
+
+    /**
+     * The Set Coded Font Local control sequence activates a coded font and
+     * specifies the character attributes to be used. This is a modal control
+     * sequence.
+     *
+     * @param font
+     *            The font local identifier.
+     * @param afpdata
+     *            The output stream to which data should be written.
+     */
+    private void setCodedFont(byte font, ByteArrayOutputStream afpdata) {
+
+        // Avoid unnecessary specification of the font
+        if (String.valueOf(font).equals(_currentFont)) {
+            return;
+        } else {
+            _currentFont = String.valueOf(font);
+        }
+
+        afpdata.write(new byte[] { 0x03, (byte) 0xF1, font, }, 0, 3);
+
+    }
+
+    /**
+     * Establishes the current presentation position on the baseline at a new
+     * I-axis coordinate, which is a specified number of measurement units from
+     * the B-axis. There is no change to the current B-axis coordinate.
+     *
+     * @param coordinate
+     *            The coordinate for the inline move.
+     * @param afpdata
+     *            The output stream to which data should be written.
+     */
+    private void absoluteMoveInline(int coordinate,
+        ByteArrayOutputStream afpdata) {
+
+        byte[] b = BinaryUtils.convert(coordinate, 2);
+
+        afpdata.write(new byte[] { 0x04, (byte) 0xC7, b[0], b[1], }, 0, 4);
+
+        _currentXCoordinate = coordinate;
+
+    }
+
+    /**
+     * Establishes the baseline and the current presentation position at a new
+     * B-axis coordinate, which is a specified number of measurement units from
+     * the I-axis. There is no change to the current I-axis coordinate.
+     *
+     * @param coordinate
+     *            The coordinate for the baseline move.
+     * @param afpdata
+     *            The output stream to which data should be written.
+     */
+    private void absoluteMoveBaseline(int coordinate,
+        ByteArrayOutputStream afpdata) {
+
+        byte[] b = BinaryUtils.convert(coordinate, 2);
+
+        afpdata.write(new byte[] { 0x04, (byte) 0xD3, b[0], b[1], }, 0, 4);
+
+        _currentYCoordinate = coordinate;
+
+    }
+
+    /**
+     * The Transparent Data control sequence contains a sequence of code points
+     * that are presented without a scan for embedded control sequences.
+     *
+     * @param data
+     *            The text data to add.
+     * @param afpdata
+     *            The output stream to which data should be written.
+     */
+    private void addTransparentData(byte[] data, ByteArrayOutputStream afpdata) {
+
+        // Calculate the length
+        int l = data.length + 2;
+
+        if (l > 255) {
+            // Check that we are not exceeding the maximum length
+            throw new IllegalArgumentException(
+                "Transparent data is longer than 253 bytes: " + data);
+        }
+
+        afpdata.write(new byte[] { BinaryUtils.convert(l)[0], (byte) 0xDB, },
+            0, 2);
+
+        afpdata.write(data, 0, data.length);
+
+    }
+
+    /**
+     * Draws a line of specified length and specified width in the B-direction
+     * from the current presentation position. The location of the current
+     * presentation position is unchanged.
+     *
+     * @param length
+     *            The length of the rule.
+     * @param width
+     *            The width of the rule.
+     * @param afpdata
+     *            The output stream to which data should be written.
+     */
+    private void drawBaxisRule(int length, int width,
+        ByteArrayOutputStream afpdata) {
+
+        afpdata.write(new byte[] { 0x07, // Length
+            (byte) 0xE7, // Type
+        }, 0, 2);
+
+        // Rule length
+        byte[] data1 = BinaryUtils.shortToByteArray((short) length);
+        afpdata.write(data1, 0, data1.length);
+        // Rule width
+        byte[] data2 = BinaryUtils.shortToByteArray((short) width);
+        afpdata.write(data2, 0, data2.length);
+        // Rule width fraction
+        afpdata.write(0x00);
+
+    }
+
+    /**
+     * Draws a line of specified length and specified width in the I-direction
+     * from the current presentation position. The location of the current
+     * presentation position is unchanged.
+     *
+     * @param length
+     *            The length of the rule.
+     * @param width
+     *            The width of the rule.
+     * @param afpdata
+     *            The output stream to which data should be written.
+     */
+    private void drawIaxisRule(int length, int width,
+        ByteArrayOutputStream afpdata) {
+
+        afpdata.write(new byte[] { 0x07, // Length
+            (byte) 0xE5, // Type
+        }, 0, 2);
+
+        // Rule length
+        byte[] data1 = BinaryUtils.shortToByteArray((short) length);
+        afpdata.write(data1, 0, data1.length);
+        // Rule width
+        byte[] data2 = BinaryUtils.shortToByteArray((short) width);
+        afpdata.write(data2, 0, data2.length);
+        // Rule width fraction
+        afpdata.write(0x00);
+
+    }
+
+    /**
+     * Create the presentation text data for the byte array of data.
+     *
+     * @param fontNumber
+     *            The font resource identifier.
+     * @param x
+     *            The x coordinate for the text data.
+     * @param y
+     *            The y coordinate for the text data.
+     * @param orientation
+     *            The orientation of the text data.
+     * @param col
+     *            The text color.
+     * @param vsci
+     *            The variable space character increment.
+     * @param ica
+     *            The inter character adjustment.
+     * @param data
+     *            The text data to be created.
+     * @throws MaximumSizeExceededException
+     */
+    public void createTextData(int fontNumber, int x, int y, int orientation,
+        AFPFontColor col, int vsci, int ica, byte[] data)
+        throws MaximumSizeExceededException {
+
+        ByteArrayOutputStream afpdata = new ByteArrayOutputStream();
+
+        if (_currentOrientation != orientation) {
+            setTextOrientation(orientation, afpdata);
+            _currentOrientation = orientation;
+            _currentXCoordinate = -1;
+            _currentYCoordinate = -1;
+        }
+
+        // Avoid unnecessary specification of the Y co-ordinate
+        if (y != _currentYCoordinate) {
+            absoluteMoveBaseline(y, afpdata);
+            _currentXCoordinate = -1;
+        }
+
+        // Avoid unnecessary specification of the X co-ordinate
+        if (x != _currentXCoordinate) {
+            absoluteMoveInline(x, afpdata);
+        }
+
+        // Avoid unnecessary specification of the variable space increment
+        if (vsci != _currentVariableSpaceCharacterIncrement) {
+            setVariableSpaceCharacterIncrement(vsci, afpdata);
+            _currentVariableSpaceCharacterIncrement = vsci;
+        }
+
+        // Avoid unnecessary specification of the inter character adjustment
+        if (ica != _currentInterCharacterAdjustment) {
+            setInterCharacterAdjustment(ica, afpdata);
+            _currentInterCharacterAdjustment = ica;
+        }
+
+        // Avoid unnecessary specification of the text color
+        if (!col.equals(_currentColor)) {
+            setExtendedTextColor(col, afpdata);
+            _currentColor.setTo(col);
+        }
+
+        setCodedFont(BinaryUtils.convert(fontNumber)[0], afpdata);
+        addTransparentData(data, afpdata);
+        _currentXCoordinate = -1;
+
+        int s = afpdata.size();
+
+        if (_baos.size() + s > MAX_SIZE) {
+            _currentXCoordinate = -1;
+            _currentYCoordinate = -1;
+            throw new MaximumSizeExceededException();
+        }
+
+        byte[] outputdata = afpdata.toByteArray();
+        _baos.write(outputdata, 0, outputdata.length);
+
+    }
+
+    /**
+     * Drawing of lines using the starting and ending coordinates, thickness and
+     * colour arguments.
+     *
+     * @param x1
+     *            The starting X coordinate.
+     * @param y1
+     *            The starting Y coordinate.
+     * @param x2
+     *            The ending X coordinate.
+     * @param y2
+     *            The ending Y coordinate.
+     * @param thickness
+     *            The line thickness.
+     * @param orientation
+     *            The orientation of the text data.
+     * @param col
+     *            The text color.
+     */
+    public void createLineData(int x1, int y1, int x2, int y2, int thickness,
+        int orientation, AFPFontColor col) throws MaximumSizeExceededException {
+
+        ByteArrayOutputStream afpdata = new ByteArrayOutputStream();
+
+        if (_currentOrientation != orientation) {
+            setTextOrientation(orientation, afpdata);
+            _currentOrientation = orientation;
+        }
+
+        // Avoid unnecessary specification of the Y coordinate
+        if (y1 != _currentYCoordinate) {
+            absoluteMoveBaseline(y1, afpdata);
+        }
+
+        // Avoid unnecessary specification of the X coordinate
+        if (x1 != _currentXCoordinate) {
+            absoluteMoveInline(x1, afpdata);
+        }
+
+        if (!col.equals(_currentColor)) {
+            setExtendedTextColor(col, afpdata);
+            _currentColor.setTo(col);
+        }
+
+        if (y1 == y2) {
+            drawIaxisRule(x2 - x1, thickness, afpdata);
+        } else if (x1 == x2) {
+            drawBaxisRule(y2 - y1, thickness, afpdata);
+        } else {
+            return;
+        }
+
+        int s = afpdata.size();
+
+        if (_baos.size() + s > MAX_SIZE) {
+            _currentXCoordinate = -1;
+            _currentYCoordinate = -1;
+            throw new MaximumSizeExceededException();
+        }
+
+        byte[] outputdata = afpdata.toByteArray();
+        _baos.write(outputdata, 0, outputdata.length);
+
+    }
+
+    /**
+     * The Set Text Orientation control sequence establishes the I-direction and
+     * B-direction for the subsequent text. This is a modal control sequence.
+     *
+     * Semantics: This control sequence specifies the I-axis and B-axis
+     * orientations with respect to the Xp-axis for the current Presentation
+     * Text object. The orientations are rotational values expressed in degrees
+     * and minutes.
+     *
+     * @param orientation
+     *            The text orientation (0,90, 180, 270).
+     * @param afpdata
+     *            The output stream to which data should be written.
+     */
+    private void setTextOrientation(int orientation,
+        ByteArrayOutputStream afpdata) {
+
+        afpdata.write(new byte[] { 0x06, (byte) 0xF7, }, 0, 2);
+
+        switch (orientation) {
+            case 90:
+                afpdata.write(0x2D);
+                afpdata.write(0x00);
+                afpdata.write(0x5A);
+                afpdata.write(0x00);
+                break;
+            case 180:
+                afpdata.write(0x5A);
+                afpdata.write(0x00);
+                afpdata.write(0x87);
+                afpdata.write(0x00);
+                break;
+            case 270:
+                afpdata.write(0x87);
+                afpdata.write(0x00);
+                afpdata.write(0x00);
+                afpdata.write(0x00);
+                break;
+            default:
+                afpdata.write(0x00);
+                afpdata.write(0x00);
+                afpdata.write(0x2D);
+                afpdata.write(0x00);
+                break;
+        }
+
+    }
+
+    /**
+     * The Set Extended Text Color control sequence specifies a color value and
+     * defines the color space and encoding for that value.  The specified color
+     * value is applied to foreground areas of the text presentation space.
+     * This is a modal control sequence.
+     *
+     * @param col
+     *            The color to be set.
+     * @param afpdata
+     *            The output stream to which data should be written.
+     */
+    private void setExtendedTextColor(AFPFontColor col,
+        ByteArrayOutputStream afpdata) {
+
+        afpdata.write(new byte[] {
+              15                // Control sequence length
+            , (byte)0x81         // Control sequence function type
+            , 0x00              // Reserved; must be zero
+            , 0x01              // Color space - 0x01 = RGB
+            , 0x00              // Reserved; must be zero
+            , 0x00              // Reserved; must be zero
+            , 0x00              // Reserved; must be zero
+            , 0x00              // Reserved; must be zero
+            , 8                         // Number of bits in component 1
+            , 8                         // Number of bits in component 2
+            , 8                         // Number of bits in component 3
+            , 0                         // Number of bits in component 4
+            , (byte)(col.getRed())   // Red intensity
+            , (byte)(col.getGreen()) // Green intensity
+            , (byte)(col.getBlue())  // Blue intensity
+        }, 0, 15);
+
+    }
+
+    /**
+     * //TODO
+     * This is a modal control sequence.
+     *
+     * @param incr
+     *            The increment to be set.
+     * @param afpdata
+     *            The output stream to which data should be written.
+     */
+    private void setVariableSpaceCharacterIncrement(int incr,
+        ByteArrayOutputStream afpdata) {
+
+        byte[] b = BinaryUtils.convert(incr, 2);
+
+        afpdata.write(new byte[] {
+              4                  // Control sequence length
+            , (byte)0xC5         // Control sequence function type
+            , b[0]
+            , b[1]
+        }, 0, 4);
+
+    }
+
+    /**
+     * //TODO
+     * This is a modal control sequence.
+     *
+     * @param incr
+     *            The increment to be set.
+     * @param afpdata
+     *            The output stream to which data should be written.
+     */
+    private void setInterCharacterAdjustment(int incr,
+        ByteArrayOutputStream afpdata) {
+
+        byte[] b = BinaryUtils.convert(Math.abs(incr), 2);
+
+        afpdata.write(new byte[] {
+              5                  // Control sequence length
+            , (byte)0xC3         // Control sequence function type
+            , b[0]
+            , b[1]
+            , (byte)(incr >= 0 ? 0 : 1) // Direction
+        }, 0, 5);
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for
+     * the text data.
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = _baos.toByteArray();
+        byte[] size = BinaryUtils.convert(data.length - 1, 2);
+        data[1] = size[0];
+        data[2] = size[1];
+
+        os.write(data);
+
+    }
+
+    /**
+     * A control sequence is a sequence of bytes that specifies a control
+     * function. A control sequence consists of a control sequence introducer
+     * and zero or more parameters. The control sequence can extend multiple
+     * presentation text data objects, but must eventually be terminated. This
+     * method terminates the control sequence.
+     *
+     * @throws MaximumSizeExceededException
+     */
+    public void endControlSequence() throws MaximumSizeExceededException {
+
+        byte[] data = new byte[2];
+        data[0] = 0x02;
+        data[1] = (byte) 0xF8;
+
+        if (data.length + _baos.size() > MAX_SIZE) {
+            throw new MaximumSizeExceededException();
+        }
+
+        _baos.write(data, 0, data.length);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/PresentationTextDescriptor.java b/src/sandbox/org/apache/fop/render/afp/modca/PresentationTextDescriptor.java
new file mode 100644 (file)
index 0000000..a205f02
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * The Presentation Text Descriptor specifies the units of measure for the
+ * Presentation Text object space, the size of the Presentation Text object
+ * space, and the initial values for modal parameters, called initial text
+ * conditions. Initial values not provided are defaulted by the controlling
+ * environment or the receiving device.
+ *
+ * The Presentation Text Descriptor provides the following initial values:
+ * - Unit base
+ * - Xp-units per unit base
+ * - Yp-units per unit base
+ * - Xp-extent of the presentation space
+ * - Yp-extent of the presentation space
+ * - Initial text conditions.
+ *
+ * The initial text conditions are values provided by the Presentation Text
+ * Descriptor to initialize the modal parameters of the control sequences.
+ * Modal control sequences typically are characterized by the word set in
+ * the name of the control sequence. Modal parameters are identified as such
+ * in their semantic descriptions.
+ *
+ */
+public class PresentationTextDescriptor extends AbstractAFPObject {
+
+    private int _width = 0;
+    private int _height = 0;
+
+    /**
+     * Constructor a PresentationTextDescriptor for the specified
+     * width and height.
+     * @param width The width of the page.
+     * @param height The height of the page.
+     */
+    public PresentationTextDescriptor(int width, int height) {
+
+        _width = width;
+        _height = height;
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the Presentation Text Descriptor
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[] {
+            0x5A,
+            0x00,
+            0x16,
+            (byte) 0xD3,
+            (byte) 0xB1,
+            (byte) 0x9B,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x09,
+            0x60,
+            0x09,
+            0x60,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+            0x00,
+        };
+
+        byte[] x = BinaryUtils.convert(_width, 3);
+        data[15] = x[0];
+        data[16] = x[1];
+        data[17] = x[2];
+
+        byte[] y = BinaryUtils.convert(_height, 3);
+        data[18] = y[0];
+        data[19] = y[1];
+        data[20] = y[2];
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/PresentationTextObject.java b/src/sandbox/org/apache/fop/render/afp/modca/PresentationTextObject.java
new file mode 100644 (file)
index 0000000..ce54de1
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import org.apache.fop.render.afp.AFPFontColor;
+
+/**
+ * The Presentation Text object is the data object used in document processing
+ * environments for representing text which has been prepared for presentation.
+ * Text, as used here, means an ordered string of characters, such as graphic
+ * symbols, numbers, and letters, that are suitable for the specific purpose of
+ * representing coherent information. Text which has been prepared for
+ * presentation has been reduced to a primitive form through explicit
+ * specification of the characters and their placement in the presentation
+ * space. Control sequences which designate specific control functions may be
+ * embedded within the text. These functions extend the primitive form by
+ * applying specific characteristics to the text when it is presented. The
+ * collection of the graphic characters and control codes is called Presentation
+ * Text, and the object that contains the Presentation Text is called the
+ * PresentationText object.
+ *
+ */
+public class PresentationTextObject extends AbstractNamedAFPObject {
+
+    /**
+     * Default name for the presentation text object
+     */
+    private static final String DEFAULT_NAME = "PTO00001";
+
+    private PresentationTextData currentPresentationTextData = null;
+
+    private ArrayList presentationTextData = new ArrayList();
+
+    /**
+     * Default constructor for the PresentationTextObject
+     */
+    public PresentationTextObject() {
+
+        this(DEFAULT_NAME);
+
+    }
+
+    /**
+     * Construct a new PresentationTextObject for the specified name argument,
+     * the name should be an 8 character identifier.
+     */
+    public PresentationTextObject(String name) {
+
+        super(name);
+
+    }
+
+    /**
+     * Create the presentation text data for the byte array of data.
+     *
+     * @param fontNumber
+     *            The font resource identifier.
+     * @param x
+     *            The x coordinate for the text data.
+     * @param y
+     *            The y coordinate for the text data.
+     * @param col
+     *            The text color.
+     * @param vsci
+     *            The variable space character increment.
+     * @param ica
+     *            The inter character increment.
+     * @param data
+     *            The text data to be created.
+     */
+    public void createTextData(int fontNumber, int x, int y, AFPFontColor col, int vsci, int ica, byte[] data) {
+
+        // Use a default orientation of zero
+        createTextData(fontNumber, x, y, 0, col, vsci, ica, data);
+
+    }
+
+    /**
+     * Create the presentation text data for the byte array of data.
+     *
+     * @param fontNumber
+     *            The font resource identifier.
+     * @param x
+     *            The x coordinate for the text data.
+     * @param y
+     *            The y coordinate for the text data.
+     * @param orientation
+     *            The orientation of the text data.
+     * @param col
+     *            The text color.
+     * @param vsci
+     *            The variable space character increment.
+     * @param ica
+     *            The inter character adjustment.
+     * @param data
+     *            The text data to be created.
+     */
+    public void createTextData(int fontNumber, int x, int y, int orientation,
+        AFPFontColor col, int vsci, int ica, byte[] data) {
+
+        if (currentPresentationTextData == null) {
+            startPresentationTextData();
+        }
+
+        try {
+
+            currentPresentationTextData.createTextData(fontNumber, x, y,
+                orientation, col, vsci, ica, data);
+
+        } catch (MaximumSizeExceededException msee) {
+
+            endPresentationTextData();
+            createTextData(fontNumber, x, y, orientation, col, vsci, ica, data);
+
+        }
+
+    }
+
+    /**
+     * Drawing of lines using the starting and ending coordinates, thickness.
+     *
+     * @param x1
+     *            The first x coordinate of the line.
+     * @param y1
+     *            The first y coordinate of the line.
+     * @param x2
+     *            The second x coordinate of the line.
+     * @param y2
+     *            The second y coordinate of the line.
+     * @param thickness
+     *            The thickness of the line.
+     * @param col
+     *            The text color.
+     */
+    public void createLineData(int x1, int y1, int x2, int y2, int thickness, AFPFontColor col) {
+        // Default orientation
+        createLineData(x1, y1, x2, y2, thickness, 0, col);
+    }
+
+    /**
+     * Drawing of lines using the starting and ending coordinates, thickness and
+     * orientation arguments.
+     *
+     * @param x1
+     *            The first x coordinate of the line.
+     * @param y1
+     *            The first y coordinate of the line.
+     * @param x2
+     *            The second x coordinate of the line.
+     * @param y2
+     *            The second y coordinate of the line.
+     * @param thickness
+     *            The thickness of the line.
+     * @param orientation
+     *            The orientation of the line.
+     * @param col
+     *            The text color.
+     */
+    public void createLineData(int x1, int y1, int x2, int y2, int thickness,
+        int orientation, AFPFontColor col) {
+
+        if (currentPresentationTextData == null) {
+            startPresentationTextData();
+        }
+
+        try {
+
+            currentPresentationTextData.createLineData(x1, y1, x2, y2,
+                thickness, orientation, col);
+
+        } catch (MaximumSizeExceededException msee) {
+
+            endPresentationTextData();
+            createLineData(x1, y1, x2, y2, thickness, orientation, col);
+
+        }
+
+    }
+
+    /**
+     * Helper method to mark the start of the presentation text data
+     */
+    private void startPresentationTextData() {
+
+        if (presentationTextData.size() == 0) {
+            currentPresentationTextData = new PresentationTextData(true);
+        } else {
+            currentPresentationTextData = new PresentationTextData();
+        }
+
+        presentationTextData.add(currentPresentationTextData);
+
+    }
+
+    /**
+     * Helper method to mark the end of the presentation text data
+     */
+    private void endPresentationTextData() {
+
+        currentPresentationTextData = null;
+
+    }
+
+    /**
+     * Accessor method to write the AFP datastream for the PresentationTextObject.
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        writeStart(os);
+
+        writeObjectList(presentationTextData, os);
+
+        writeEnd(os);
+
+    }
+
+    public String getName() {
+
+        return _name;
+
+    }
+
+    /**
+     * Helper method to write the start of the presenation text object.
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA8; // Structured field id byte 2
+        data[5] = (byte) 0x9B; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the presenation text object.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA9; // Structured field id byte 2
+        data[5] = (byte) 0x9B; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * A control sequence is a sequence of bytes that specifies a control
+     * function. A control sequence consists of a control sequence introducer
+     * and zero or more parameters. The control sequence can extend multiple
+     * presentation text data objects, but must eventually be terminated. This
+     * method terminates the control sequence.
+     */
+    public void endControlSequence() {
+
+        if (currentPresentationTextData == null) {
+            startPresentationTextData();
+        }
+
+        try {
+
+            currentPresentationTextData.endControlSequence();
+
+        } catch (MaximumSizeExceededException msee) {
+
+            endPresentationTextData();
+            endControlSequence();
+
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/ResourceGroup.java b/src/sandbox/org/apache/fop/render/afp/modca/ResourceGroup.java
new file mode 100644 (file)
index 0000000..0cfb538
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Resource Group contains a set of overlays.
+ */
+public final class ResourceGroup extends AbstractNamedAFPObject {
+
+    /**
+     * Default name for the resource group
+     */
+    private static final String DEFAULT_NAME = "RG000001";
+
+
+    /**
+     * The overlays contained in this resource group
+     */
+    private List _overlays = new ArrayList();
+
+    public ResourceGroup() {
+
+        this(DEFAULT_NAME);
+
+    }
+
+    /**
+     * Constructor for the ResourceGroup, this takes a
+     * name parameter which must be 8 characters long.
+     * @param name the resource group name
+     */
+    public ResourceGroup(String name) {
+
+        super(name);
+
+    }
+
+    /**
+     * Adds an overlay to the resource group
+     * @param overlay the overlay to add
+     */
+    public void addOverlay(Overlay overlay) {
+        _overlays.add(overlay);
+    }
+
+    /**
+     * Returns the list of overlays
+     * @return the list of overlays
+     */
+    public List getOverlays() {
+        return _overlays;
+    }
+
+    /**
+     * Accessor method to obtain write the AFP datastream for
+     * the resource group.
+     * @param os The stream to write to
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os)
+        throws IOException {
+
+        writeStart(os);
+
+        writeObjectList(_overlays, os);
+
+        writeEnd(os);
+
+    }
+
+    /**
+     * Helper method to write the start of the resource group.
+     * @param os The stream to write to
+     */
+    private void writeStart(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA8; // Structured field id byte 2
+        data[5] = (byte) 0xC6; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+    /**
+     * Helper method to write the end of the resource group.
+     * @param os The stream to write to
+     */
+    private void writeEnd(OutputStream os)
+        throws IOException {
+
+        byte[] data = new byte[17];
+
+        data[0] = 0x5A; // Structured field identifier
+        data[1] = 0x00; // Length byte 1
+        data[2] = 0x10; // Length byte 2
+        data[3] = (byte) 0xD3; // Structured field id byte 1
+        data[4] = (byte) 0xA9; // Structured field id byte 2
+        data[5] = (byte) 0xC6; // Structured field id byte 3
+        data[6] = 0x00; // Flags
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        for (int i = 0; i < _nameBytes.length; i++) {
+
+            data[9 + i] = _nameBytes[i];
+
+        }
+
+        os.write(data);
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/TagLogicalElement.java b/src/sandbox/org/apache/fop/render/afp/modca/TagLogicalElement.java
new file mode 100644 (file)
index 0000000..ca28154
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.fop.render.afp.tools.BinaryUtils;
+
+/**
+ * A Tag Logical Element structured field assigns an attribute name and an
+ * attribute value to a page or page group. The Tag Logical Element structured
+ * field may be embedded directly in the page or page group, or it may reference
+ * the page or page group from a document index. When a Tag Logical Element
+ * structured field references a page or is embedded in a page following the
+ * active environment group, it is associated with the page. When a Tag Logical
+ * Element structured field references a page group or is embedded in a page
+ * group following the Begin Named Page Group structured field, it is associated
+ * with the page group. When a Tag Logical Element structured field is associated
+ * with a page group, the parameters of the Tag Logical Element structured field
+ * are inherited by all pages in the page group and by all other page groups
+ * that are nested in the page group. The scope of a Tag Logical Element is
+ * determined by its position with respect to other TLEs that reference, or are
+ * embedded in, the same page or page group. The Tag Logical Element structured
+ * field does not provide any presentation specifications and therefore has no
+ * effect on the appearance of a document when it is presented.
+ * <p/>
+ */
+public class TagLogicalElement extends AbstractAFPObject {
+
+    /**
+     * Name of the key, used within the TLE
+     */
+    private String _tleName = null;
+
+    /**
+     * Value returned by the key
+     */
+    private String _tleValue = null;
+
+    /**
+     * Byte representaion of the name
+     */
+    private byte[] _tleByteName = null;
+
+    /**
+     * Byte representaion of the value
+     */
+    private byte[] _tleByteValue = null;
+
+    /**
+     * Construct a tag logical element with the name and value specified.
+     * @param name the name of the tag logical element
+     * @param value the value of the tag logical element
+     */
+    public TagLogicalElement(String name, String value) {
+
+        _tleName = name;
+        _tleValue = value;
+
+        try {
+
+            _tleByteName = name.getBytes(AFPConstants.EBCIDIC_ENCODING);
+            _tleByteValue = value.getBytes(AFPConstants.EBCIDIC_ENCODING);
+
+        } catch (UnsupportedEncodingException usee) {
+
+            _tleByteName = name.getBytes();
+            _tleByteValue = value.getBytes();
+            log.warn(
+                "Constructor:: UnsupportedEncodingException translating the name "
+                + name);
+
+        }
+
+    }
+
+    /**
+     * Accessor method to obtain the byte array AFP datastream for the
+     * TagLogicalElement.
+     * @param os The outputsteam stream
+     * @throws java.io.IOException
+     */
+    public void writeDataStream(OutputStream os) throws IOException {
+
+        byte[] data = new byte[17 + _tleName.length() + _tleValue.length()];
+
+        data[0] = 0x5A;
+        // Set the total record length
+        byte[] rl1 =
+            BinaryUtils.convert(16 + _tleName.length() + _tleValue.length(), 2);
+        //Ignore first byte
+        data[1] = rl1[0];
+        data[2] = rl1[1];
+
+        // Structured field ID for a TLE
+        data[3] = (byte) 0xD3;
+        data[4] = (byte) 0xA0;
+        data[5] = (byte) 0x90;
+
+        data[6] = 0x00; // Reserved
+        data[7] = 0x00; // Reserved
+        data[8] = 0x00; // Reserved
+
+        //Use 2 triplets, attrubute name and value (the key for indexing)
+
+        byte[] rl2 = BinaryUtils.convert(_tleName.length() + 4, 1);
+        data[9] = rl2[0]; // length of the triplet, including this field
+        data[10] = 0x02; //Identifies it as a FQN triplet
+        data[11] = 0x0B; // GID format
+        data[12] = 0x00;
+
+        int pos = 13;
+        for (int i = 0; i < _tleByteName.length; i++) {
+            data[pos++] = _tleByteName[i];
+        }
+
+        byte[] rl3 = BinaryUtils.convert(_tleByteValue.length + 4, 1);
+        data[pos++] = rl3[0]; // length of the triplet, including this field
+        data[pos++] = 0x36; //Identifies the triplet, attribute value
+        data[pos++] = 0x00; // Reserved
+        data[pos++] = 0x00; // Reserved
+
+        for (int i = 0; i < _tleByteValue.length; i++) {
+            data[pos++] = _tleByteValue[i];
+        }
+        os.write(data);
+
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/modca/TagLogicalElementBean.java b/src/sandbox/org/apache/fop/render/afp/modca/TagLogicalElementBean.java
new file mode 100644 (file)
index 0000000..1784dfe
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.modca;
+
+/**
+ * The TagLogicalElementBean provides a bean for holding the attributes of
+ * a tag logical element as key value pairs.
+ * <p/>
+ */
+public class TagLogicalElementBean {
+
+    /** The key attribute */
+    private String _key;
+
+    /** The value attribute */
+    private String _value;
+
+    /**
+     * Constructor for the TagLogicalElementBean.
+     * @param key the key attribute
+     * @param value the value attribute
+     */
+    public TagLogicalElementBean(String key, String value) {
+        _key = key;
+        _value = value;
+    }
+
+    /**
+     * Getter for the key attribute.
+     * @return the key
+     */
+    public String getKey() {
+        return _key;
+    }
+
+    /**
+     * Getter for the value attribute.
+     * @return the value
+     */
+    public String getValue() {
+        return _value;
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/tools/BinaryUtils.java b/src/sandbox/org/apache/fop/render/afp/tools/BinaryUtils.java
new file mode 100644 (file)
index 0000000..4dead37
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.tools;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Library of utility useful conversion methods.
+ *
+ */
+public final class BinaryUtils {
+
+    /**
+     * Convert an int into the corresponding byte array by encoding each
+     * two hexadecimal digits as a char. This will return a byte array
+     * to the length specified by bufsize.
+     * @param integer The int representation.
+     * @param bufsize The required byte array size.
+     */
+    public static byte[] convert(int integer, int bufsize) {
+
+        StringBuffer buf = new StringBuffer(Integer.toHexString(integer));
+        if (buf.length() % 2 == 0) {
+            // Ignore even number of digits
+        } else {
+            // Convert to an even number of digits
+            buf.insert(0, "0");
+        }
+        int size = buf.length() / 2;
+        while (size < bufsize) {
+            buf.insert(0, "00");
+            size++;
+        };
+        return convert(buf.toString());
+
+    }
+
+    /**
+     * Convert an int into the corresponding byte array by encoding each
+     * two hexadecimal digits as a char.
+     * @param integer The int representation
+     */
+    public static byte[] convert(int integer) {
+
+        return convert(Integer.toHexString(integer));
+
+    }
+
+    /**
+     * Convert a String of hexadecimal digits into the corresponding
+     * byte array by encoding each two hexadecimal digits as a byte.
+     * @param digits The hexadecimal digits representation.
+     */
+    public static byte[] convert(String digits) {
+
+        if (digits.length() % 2 == 0) {
+            // Even number of digits, so ignore
+        } else {
+            // Convert to an even number of digits
+            digits = "0" + digits;
+        }
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        for (int i = 0; i < digits.length(); i += 2) {
+            char c1 = digits.charAt(i);
+            char c2 = digits.charAt(i + 1);
+            byte b = 0;
+            if ((c1 >= '0') && (c1 <= '9'))
+                b += ((c1 - '0') * 16);
+            else if ((c1 >= 'a') && (c1 <= 'f'))
+                b += ((c1 - 'a' + 10) * 16);
+            else if ((c1 >= 'A') && (c1 <= 'F'))
+                b += ((c1 - 'A' + 10) * 16);
+            else
+                throw new IllegalArgumentException("Bad hexadecimal digit");
+            if ((c2 >= '0') && (c2 <= '9'))
+                b += (c2 - '0');
+            else if ((c2 >= 'a') && (c2 <= 'f'))
+                b += (c2 - 'a' + 10);
+            else if ((c2 >= 'A') && (c2 <= 'F'))
+                b += (c2 - 'A' + 10);
+            else
+                throw new IllegalArgumentException("Bad hexadecimal digit");
+            baos.write(b);
+        }
+        return (baos.toByteArray());
+
+    }
+
+    /**
+     * Convert the specified short into a byte array.
+     * @param value The value to be converted.
+     * @param array The array to receive the data.
+     * @param offset The offset into the byte array for the start of the value.
+     */
+    public static void shortToByteArray(
+        short value,
+        byte[] array,
+        int offset) {
+        array[offset] = (byte) (value >>> 8);
+        array[offset + 1] = (byte) value;
+    }
+
+    /**
+     * Convert the specified short into a byte array.
+     * @param value The value to be converted.
+     * @return The byte array
+     */
+    public static byte[] shortToByteArray(short value) {
+        byte[] serverValue = new byte[2];
+        shortToByteArray(value, serverValue, 0);
+        return serverValue;
+    }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/tools/DTDEntityResolver.java b/src/sandbox/org/apache/fop/render/afp/tools/DTDEntityResolver.java
new file mode 100644 (file)
index 0000000..94f4c46
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.tools;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.fop.render.afp.exceptions.FontRuntimeException;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+/**
+ * An entity resolver for both DOM and SAX models of the SAX document.
+ * <p>
+ * The entity resolver only handles queries for the DTD. It will find any URI
+ * with a recognised public id and return an {@link org.xml.sax.InputSource}.
+ * <p>
+ * @author <a href="mailto:joe@exubero.com">Joe Schmetzer</a>
+ */
+public class DTDEntityResolver implements EntityResolver {
+
+    /** Public ID for the AFP fonts 1.0 DTD. */
+    public static final String AFP_DTD_1_0_ID = "-//APACHE/DTD AFP Installed Font Definition DTD 1.0//EN";
+
+    /** Resource location for the AFP fonts 1.0 DTD. */
+    public static final String AFP_DTD_1_0_RESOURCE =  "afp-fonts-1.0.dtd";
+
+    /** Public ID for the AFP fonts 1.1 DTD. */
+    public static final String AFP_DTD_1_1_ID = "-//APACHE/DTD AFP Installed Font Definition DTD 1.1//EN";
+
+    /** Resource location for the AFP fonts 1.1 DTD. */
+    public static final String AFP_DTD_1_1_RESOURCE =  "afp-fonts-1.1.dtd";
+
+    /** Public ID for the AFP fonts 1.2 DTD. */
+    public static final String AFP_DTD_1_2_ID = "-//APACHE/DTD AFP Installed Font Definition DTD 1.2//EN";
+
+    /** Resource location for the AFP fonts 1.2 DTD. */
+    public static final String AFP_DTD_1_2_RESOURCE =  "afp-fonts-1.2.dtd";
+
+    /**
+     * Resolve the combination of system and public identifiers.
+     * If this resolver recognises the publicId, it will handle the resolution
+     * from the classpath, otherwise it will return null and allow the default
+     * resolution to occur.
+     *
+     * @param publicId the public identifier to use
+     * @param systemId the system identifier to resolve
+     * @return An input source to the entity or null if not handled
+     * @throws IOException an error reading the stream
+     */
+    public InputSource resolveEntity(String publicId, String systemId)
+    throws IOException {
+
+        URL resource = null;
+        if( AFP_DTD_1_2_ID.equals(publicId) ) {
+            resource = getResource( AFP_DTD_1_2_RESOURCE );
+        } else if( AFP_DTD_1_1_ID.equals(publicId) ) {
+            resource = getResource( AFP_DTD_1_1_RESOURCE );
+        } else if( AFP_DTD_1_0_ID.equals(publicId) ) {
+            throw new FontRuntimeException(
+                "The AFP Installed Font Definition 1.0 DTD is not longer supported" );
+        } else if( systemId != null && systemId.indexOf("afp-fonts.dtd") >= 0 ) {
+            throw new FontRuntimeException(
+                "The AFP Installed Font Definition DTD must be specified using the public id" );
+        } else {
+            return null;
+        }
+
+        InputSource inputSource = new InputSource( resource.openStream() );
+        inputSource.setPublicId( publicId );
+        inputSource.setSystemId( systemId );
+
+        return inputSource;
+    }
+
+    /**
+     * Returns the URL of a resource on the classpath
+     * @param resourceName the path to the resource relative to the root of the
+     * classpath.
+     * @return the URL of the required resource
+     * @throws FontRuntimeException if the resource could not be found.
+     */
+    private URL getResource( String resourcePath ) {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        if (cl == null) {
+            cl = ClassLoader.getSystemClassLoader();
+        }
+
+        URL resource = cl.getResource( resourcePath );
+        if( resource == null ) {
+            throw new FontRuntimeException( "Resource " + resourcePath +
+                " could not be found on the classpath" );
+        }
+
+        return resource;
+    }
+}
diff --git a/src/sandbox/org/apache/fop/render/afp/tools/StringUtils.java b/src/sandbox/org/apache/fop/render/afp/tools/StringUtils.java
new file mode 100644 (file)
index 0000000..67b5784
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.tools;
+
+/**
+ * Library of utility methods useful in dealing with strings.
+ *
+ */
+public class StringUtils {
+
+    /**
+     * Padds the string to the left with the given character for
+     * the specified length.
+     * @param input The input string.
+     * @param padding The char used for padding.
+     * @param length The length of the new string.
+     * @return The padded string.
+     */
+    public static String lpad(String input, char padding, int length) {
+
+        if (input == null) {
+            input = new String();
+        }
+
+        if (input.length() >= length) {
+            return input;
+        } else {
+            StringBuffer result = new StringBuffer();
+            int numChars = length - input.length();
+            for (int i = 0; i < numChars; i++) {
+                result.append(padding);
+            }
+            result.append(input);
+            return result.toString();
+        }
+    }
+
+    /**
+     * Padds the string to the right with the given character for
+     * the specified length.
+     * @param input The input string.
+     * @param padding The char used for padding.
+     * @param length The length of the new string.
+     * @return The padded string.
+     */
+    public static String rpad(String input, char padding, int length) {
+
+        if (input == null) {
+            input = new String();
+        }
+
+        if (input.length() >= length) {
+            return input;
+        } else {
+            StringBuffer result = new StringBuffer(input);
+            int numChars = length - input.length();
+            for (int i = 0; i < numChars; i++) {
+                result.append(padding);
+            }
+            return result.toString();
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/sandbox/org/apache/fop/render/afp/tools/StructuredFieldReader.java b/src/sandbox/org/apache/fop/render/afp/tools/StructuredFieldReader.java
new file mode 100644 (file)
index 0000000..7df320c
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.tools;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A helper class to read structured fields from a MO:DCA document. Each
+ * component of a mixed object document is explicitly defined and delimited
+ * in the data. This is accomplished through the use of MO:DCA data structures,
+ * called structured fields. Structured fields are used to envelop document
+ * components and to provide commands and information to applications using
+ * the data. Structured fields may contain one or more parameters. Each
+ * parameter provides one value from a set of values defined by the architecture.
+ * <p/>
+ * MO:DCA structured fields consist of two parts: an introducer that identifies
+ * the length and type of the structured field, and data that provides the
+ * structured field's effect. The data is contained in a set of parameters,
+ * which can consist of other data structures and data elements. The maximum
+ * length of a structured field is 32767 bytes.
+ * <p/>
+ */
+public class StructuredFieldReader {
+
+    /**
+     * The input stream to read
+     */
+    private InputStream _inputStream = null;
+
+    /**
+     * The constructor for the StructuredFieldReader
+     * @param inputStream the input stream to process
+     */
+    public StructuredFieldReader(InputStream inputStream) {
+
+        _inputStream = inputStream;
+
+    }
+
+    /**
+     * Get the next structured field as identified by the identifer
+     * parameter (this must be a valid MO:DCA structured field.
+     * @param identifier the three byte identifier
+     */
+    public byte[] getNext(byte[] identifier) throws IOException {
+
+        int bufferPointer = 0;
+        byte[] bufferData = new byte[identifier.length + 2];
+        for (int x = 0; x < identifier.length; x++) {
+            bufferData[x] = (byte) 0;
+        }
+
+        int c;
+        while ((c = _inputStream.read()) > -1) {
+
+            bufferData[bufferPointer] = (byte) c;
+
+            // Check the last characters in the buffer
+            int index = 0;
+            boolean found = true;
+
+            for (int i = identifier.length - 1; i > -1; i--) {
+
+                int p = bufferPointer - index;
+                if (p < 0) {
+                    p = bufferData.length + p;
+                }
+
+                index++;
+
+                if (identifier[i] != bufferData[p]) {
+                    found = false;
+                    break;
+                }
+
+            }
+
+            if (found) {
+
+                byte[] length = new byte[2];
+
+                int a = bufferPointer - identifier.length;
+                if (a < 0) {
+                    a = bufferData.length + a;
+                }
+
+                int b = bufferPointer - identifier.length - 1;
+                if (b < 0) {
+                    b = bufferData.length + b;
+                }
+
+                length[0] = bufferData[b];
+                length[1] = bufferData[a];
+
+                int reclength = ((length[0] & 0xFF) << 8) + (length[1] & 0xFF)  - identifier.length -2;
+
+                byte[] retval = new byte[reclength];
+
+                _inputStream.read(retval, 0, reclength);
+
+                return retval;
+
+            }
+
+            bufferPointer++;
+            if (bufferPointer >= bufferData.length) {
+                bufferPointer = 0;
+            }
+
+        }
+
+        return new byte[] {
+        };
+
+    }
+
+}