diff options
Diffstat (limited to 'src/sandbox/org/apache/fop/render/afp/AFPRenderer.java')
-rw-r--r-- | src/sandbox/org/apache/fop/render/afp/AFPRenderer.java | 1796 |
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); + } + } + +} + |