aboutsummaryrefslogtreecommitdiffstats
path: root/src/sandbox/org/apache/fop/render/afp/AFPRenderer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/sandbox/org/apache/fop/render/afp/AFPRenderer.java')
-rw-r--r--src/sandbox/org/apache/fop/render/afp/AFPRenderer.java1796
1 files changed, 1796 insertions, 0 deletions
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
index 000000000..e4505d050
--- /dev/null
+++ b/src/sandbox/org/apache/fop/render/afp/AFPRenderer.java
@@ -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);
+ }
+ }
+
+}
+