| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* $Id$ */ |
| |
| package org.apache.fop.render.afp; |
| |
| import java.awt.Color; |
| import java.awt.Point; |
| import java.awt.Rectangle; |
| import java.awt.geom.AffineTransform; |
| import java.awt.geom.Rectangle2D; |
| import java.awt.image.RenderedImage; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.UnsupportedEncodingException; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Stack; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.apache.commons.io.output.ByteArrayOutputStream; |
| |
| import org.apache.xmlgraphics.image.codec.tiff.TIFFImage; |
| import org.apache.xmlgraphics.image.loader.ImageException; |
| import org.apache.xmlgraphics.image.loader.ImageFlavor; |
| import org.apache.xmlgraphics.image.loader.ImageInfo; |
| import org.apache.xmlgraphics.image.loader.ImageManager; |
| import org.apache.xmlgraphics.image.loader.ImageSessionContext; |
| import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; |
| import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax; |
| import org.apache.xmlgraphics.image.loader.impl.ImageRendered; |
| import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; |
| import org.apache.xmlgraphics.image.loader.util.ImageUtil; |
| import org.apache.xmlgraphics.ps.ImageEncodingHelper; |
| |
| import org.apache.fop.apps.FOPException; |
| import org.apache.fop.apps.FOUserAgent; |
| import org.apache.fop.apps.MimeConstants; |
| import org.apache.fop.area.BlockParent; |
| import org.apache.fop.area.BlockViewport; |
| import org.apache.fop.area.CTM; |
| import org.apache.fop.area.OffDocumentItem; |
| import org.apache.fop.area.PageViewport; |
| import org.apache.fop.area.RegionViewport; |
| import org.apache.fop.area.Trait; |
| import org.apache.fop.area.inline.Image; |
| import org.apache.fop.area.inline.Leader; |
| import org.apache.fop.area.inline.TextArea; |
| import org.apache.fop.datatypes.URISpecification; |
| import org.apache.fop.fo.Constants; |
| import org.apache.fop.fo.extensions.ExtensionAttachment; |
| import org.apache.fop.fonts.FontInfo; |
| import org.apache.fop.fonts.FontTriplet; |
| 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.render.AbstractPathOrientedRenderer; |
| import org.apache.fop.render.AbstractState; |
| import org.apache.fop.render.Graphics2DAdapter; |
| import org.apache.fop.render.RendererContext; |
| import org.apache.fop.render.afp.extensions.AFPElementMapping; |
| import org.apache.fop.render.afp.extensions.AFPPageSetup; |
| import org.apache.fop.render.afp.fonts.AFPFont; |
| import org.apache.fop.render.afp.fonts.AFPFontInfo; |
| 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.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; |
| import org.apache.fop.render.pdf.CTMHelper; |
| |
| /** |
| * 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 possible 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 |
| * miscellaneous 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 relevant 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 { |
| |
| /** |
| * 2400 dpi renderer resolution |
| */ |
| protected static final int DPI_240_RESOLUTION = 240; |
| |
| /** |
| * 14400 dpi renderer resolution |
| */ |
| protected static final int DPI_1440_RESOLUTION = 1440; |
| |
| /** |
| * The afp factor for calculating resolutions (e.g. 72000/240 = 300) |
| */ |
| protected static final int DPI_CONVERSION_FACTOR = 72000; |
| |
| /** |
| * The afp data stream object responsible for generating afp data |
| */ |
| private AFPDataStream afpDataStream = null; |
| |
| /** |
| * The map of page segments |
| */ |
| private Map pageSegmentsMap = null; |
| |
| /** |
| * The page width |
| */ |
| private int currentPageWidth = 0; |
| |
| /** |
| * The page height |
| */ |
| private int currentPageHeight = 0; |
| |
| /** |
| * The portrait rotation |
| */ |
| private int portraitRotation = 0; |
| |
| /** |
| * The landscape rotation |
| */ |
| private int landscapeRotation = 270; |
| |
| /** |
| * 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; |
| |
| /** |
| * The output resolution |
| */ |
| private int resolution = DPI_240_RESOLUTION; |
| |
| /** drawing state */ |
| protected AFPState currentState = null; |
| |
| /** |
| * Constructor for AFPRenderer. |
| */ |
| public AFPRenderer() { |
| super(); |
| } |
| |
| private AFPState getState() { |
| if (currentState == null) { |
| currentState = new AFPState(); |
| } |
| return currentState; |
| } |
| |
| /** |
| * 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); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public void setUserAgent(FOUserAgent agent) { |
| super.setUserAgent(agent); |
| } |
| |
| /** {@inheritDoc} */ |
| public void startRenderer(OutputStream outputStream) throws IOException { |
| getState(); |
| getAFPDataStream().setPortraitRotation(portraitRotation); |
| getAFPDataStream().setLandscapeRotation(landscapeRotation); |
| getAFPDataStream().startDocument(outputStream); |
| } |
| |
| /** {@inheritDoc} */ |
| public void stopRenderer() throws IOException { |
| getAFPDataStream().endDocument(); |
| } |
| |
| /** {@inheritDoc} */ |
| 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. |
| * |
| * {@inheritDoc} |
| */ |
| public void preparePage(PageViewport page) { |
| getState().reset(); |
| |
| Rectangle2D bounds = page.getViewArea(); |
| |
| this.currentPageWidth = mpts2units(bounds.getWidth()); |
| this.currentPageHeight = mpts2units(bounds.getHeight()); |
| |
| final int pageRotation = 0; |
| getAFPDataStream().startPage(currentPageWidth, currentPageHeight, pageRotation, |
| this.resolution, this.resolution); |
| |
| renderPageObjectExtensions(page); |
| |
| if (this.pages == null) { |
| this.pages = new java.util.HashMap(); |
| } |
| this.pages.put(page, getAFPDataStream().savePage()); |
| } |
| |
| /** {@inheritDoc} */ |
| public void processOffDocumentItem(OffDocumentItem odi) { |
| // TODO |
| log.debug("NYI processOffDocumentItem(" + odi + ")"); |
| } |
| |
| /** {@inheritDoc} */ |
| public Graphics2DAdapter getGraphics2DAdapter() { |
| return new AFPGraphics2DAdapter(); |
| } |
| |
| /** {@inheritDoc} */ |
| public void startVParea(CTM ctm, Rectangle2D clippingRect) { |
| saveGraphicsState(); |
| ViewPortPos vpp; |
| if (ctm != null && clippingRect != null) { |
| AffineTransform at = new AffineTransform(CTMHelper.toPDFArray(ctm)); |
| // Set the given CTM in the graphics state |
| getState().concatenate(at); |
| vpp = new ViewPortPos(clippingRect, ctm); |
| } else { |
| vpp = new ViewPortPos(); |
| } |
| getViewPortStack().push(vpp); |
| getAFPDataStream().setOffsets(vpp.x, vpp.y, vpp.rot); |
| } |
| |
| /** {@inheritDoc} */ |
| public void endVParea() { |
| if (!getViewPortStack().isEmpty()) { |
| ViewPortPos vpp = (ViewPortPos)getViewPortStack().pop(); |
| getAFPDataStream().setOffsets(vpp.x, vpp.y, vpp.rot); |
| } |
| restoreGraphicsState(); |
| } |
| |
| /** {@inheritDoc} */ |
| protected void concatenateTransformationMatrix(AffineTransform at) { |
| if (!at.isIdentity()) { |
| getState().concatenate(at); |
| // double transX = at.getTranslateX(); |
| // double transY = at.getTranslateY(); |
| float[] srcPts = new float[] {0, 0}; |
| int rot = 0; |
| if (!getViewPortStack().isEmpty()) { |
| // new ViewPortPos(new CTM(at)); |
| ViewPortPos vpp = (ViewPortPos)getViewPortStack().peek(); |
| srcPts[0] = vpp.x; |
| srcPts[1] = vpp.y; |
| rot = vpp.rot; |
| // ViewPortPos transVpp |
| // = new ViewPortPos(Math.round(dstPts[0]), Math.round(dstPts[1]), vpp.rot); |
| // getViewPortPositions().add(transVpp); |
| // getAFPDataStream().setOffsets(transVpp.x, transVpp.y, transVpp.rot); |
| } |
| float[] dstPts = new float[2]; |
| at.transform(srcPts, 0, dstPts, 0, 1); |
| // getAFPDataStream().setOffsets(pts2units(dstPts[0]), pts2units(dstPts[1]), rot); |
| getAFPDataStream().setOffsets(Math.round(dstPts[0]), Math.round(dstPts[1]), rot); |
| // getViewPortPositions(). |
| // getViewPortPositions().add(vpp); |
| } |
| } |
| |
| // protected Rectangle2D getRegionViewportViewArea(RegionViewport port) { |
| // return port.getViewArea(); |
| // } |
| |
| // /** |
| // * 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); |
| // */ |
| // |
| // startVParea(regionReference.getCTM(), view); |
| // |
| // if (regionReference.getRegionClass() == FO_REGION_BODY) { |
| // renderBodyRegion((BodyRegion) regionReference); |
| // } else { |
| // renderRegion(regionReference); |
| // } |
| // /* |
| // * _afpDataStream.endOverlay(); |
| // */ |
| // endVParea(); |
| // } |
| // } |
| |
| /** |
| * Returns the position transform |
| * @param bp The block parent |
| * @return the position transform |
| */ |
| protected AffineTransform getPositionTransform(BlockParent bp) { |
| AffineTransform transform = super.getPositionTransform(bp); |
| // AffineTransform at = new AffineTransform(); |
| // at. |
| // Math.round(mpt / (DPI_CONVERSION_FACTOR / getResolution())); |
| // transform.concatenate(at); |
| return transform; |
| } |
| |
| /** {@inheritDoc} */ |
| protected float getBackAndBordersStartX(BlockViewport bv) { |
| return (float) (bv.getXOffset() + bv.getSpaceStart() + containingIPPosition) / 1000f; |
| } |
| |
| /** {@inheritDoc} */ |
| protected float getBackAndBordersStartY(BlockViewport bv) { |
| return (float) (bv.getYOffset() + bv.getSpaceBefore() + containingBPPosition) / 1000f; |
| } |
| |
| // /** {@inheritDoc} */ |
| // protected float getInlineAreaStartX(InlineArea area) { |
| // return currentIPPosition / 1000f; |
| // } |
| // |
| // /** {@inheritDoc} */ |
| // protected float getInlineAreaStartY(InlineArea area) { |
| // float height = area.getBPD() / 1000f; |
| // float blah = (currentBPPosition + area.getOffset() |
| // - area.getBorderAndPaddingWidthBefore() / 1000f); |
| // return height - blah; |
| // } |
| |
| /** {@inheritDoc} */ |
| // protected void handleDrawBackAndBorders(BlockViewport bv, List children) { |
| // |
| // CTM tempctm = new CTM(containingIPPosition, containingBPPosition); |
| // CTM ctm = tempctm.multiply(bv.getCTM()); |
| // |
| // // Adjust for spaces (from margin or indirectly by start-indent etc. |
| // currentIPPosition = bv.getXOffset() + bv.getSpaceStart(); |
| // currentBPPosition = bv.getYOffset() + bv.getSpaceBefore(); |
| // |
| // float bpwidth |
| // = (bv.getBorderAndPaddingWidthStart() + bv.getBorderAndPaddingWidthEnd()) / 1000f; |
| // float bpheight |
| // = (bv.getBorderAndPaddingWidthBefore() + bv.getBorderAndPaddingWidthAfter()) / 1000f; |
| // |
| // float x = (float) (bv.getXOffset() + bv.getSpaceStart() + containingIPPosition) / 1000f; |
| // float y = (float) (bv.getYOffset() + bv.getSpaceBefore() + containingBPPosition) / 1000f; |
| // |
| // //This is the content-rect |
| // float width = (float)bv.getIPD() / 1000f; |
| // float height = (float)bv.getBPD() / 1000f; |
| // |
| // drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight); |
| // |
| // // Now adjust for border/padding |
| // currentIPPosition += bv.getBorderAndPaddingWidthStart(); |
| // currentBPPosition += bv.getBorderAndPaddingWidthBefore(); |
| // |
| // Rectangle2D clippingRect = null; |
| // clippingRect |
| // = new Rectangle(currentIPPosition, currentBPPosition, bv.getIPD(), bv.getBPD()); |
| // |
| // startVParea(ctm, clippingRect); |
| // currentIPPosition = 0; |
| // currentBPPosition = 0; |
| // renderBlocks(bv, children); |
| // endVParea(); |
| // } |
| |
| /** {@inheritDoc} */ |
| public void renderPage(PageViewport pageViewport) throws IOException, FOPException { |
| getState().reset(); |
| Rectangle2D bounds = pageViewport.getViewArea(); |
| this.currentPageWidth = mpts2units(bounds.getWidth()); |
| this.currentPageHeight = mpts2units(bounds.getHeight()); |
| if (pages != null && pages.containsKey(pageViewport)) { |
| getAFPDataStream().restorePage((PageObject) pages |
| .remove(pageViewport)); |
| } else { |
| final int pageRotation = 0; |
| getAFPDataStream().startPage(currentPageWidth, currentPageHeight, pageRotation, |
| this.resolution, this.resolution); |
| |
| renderPageObjectExtensions(pageViewport); |
| } |
| // startVParea(null, null); |
| |
| getAFPDataStream().addFontsToCurrentPage(getState().getPageFonts()); |
| |
| super.renderPage(pageViewport); |
| |
| getAFPDataStream().endPage(); |
| |
| // endVParea(); |
| } |
| |
| /** {@inheritDoc} */ |
| public void clip() { |
| // TODO |
| log.debug("NYI: clip()"); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void clipRect(float x, float y, float width, float height) { |
| // TODO |
| log.debug("NYI: clipRect(x=" + x + ",y=" + y + ",width=" + width + ",height=" + height + ")"); |
| } |
| |
| /** {@inheritDoc} */ |
| public void moveTo(float x, float y) { |
| // TODO |
| log.debug("NYI: moveTo(x=" + x + ",y=" + y + ")"); |
| } |
| |
| /** {@inheritDoc} */ |
| public void lineTo(float x, float y) { |
| // TODO |
| log.debug("NYI: lineTo(x=" + x + ",y=" + y + ")"); |
| } |
| |
| /** {@inheritDoc} */ |
| public void closePath() { |
| // TODO |
| log.debug("NYI: closePath()"); |
| } |
| |
| /** {@inheritDoc} */ |
| 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()); |
| */ |
| getAFPDataStream().createLine(pts2units(x), pts2units(y), pts2units(x |
| + width), pts2units(y), pts2units(height), getState().getColor()); |
| } |
| |
| /** {@inheritDoc} */ |
| public void drawBorderLine(float x1, float y1, float x2, float y2, |
| boolean horz, boolean startOrBefore, int style, Color 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; |
| getAFPDataStream().createLine(pts2units(x1), pts2units(ym1), |
| pts2units(x2), pts2units(ym1), pts2units(h3), col); |
| getAFPDataStream().createLine(pts2units(x1), pts2units(ym2), |
| pts2units(x2), pts2units(ym2), pts2units(h3), col); |
| } else { |
| float w3 = w / 3; |
| float xm1 = x1; |
| float xm2 = xm1 + w3 + w3; |
| getAFPDataStream().createLine(pts2units(xm1), pts2units(y1), |
| pts2units(xm1), pts2units(y2), pts2units(w3), col); |
| getAFPDataStream().createLine(pts2units(xm2), pts2units(y1), |
| pts2units(xm2), pts2units(y2), pts2units(w3), col); |
| } |
| break; |
| case Constants.EN_DASHED: |
| if (horz) { |
| float w2 = 2 * h; |
| while (x1 + w2 < x2) { |
| getAFPDataStream().createLine(pts2units(x1), pts2units(y1), |
| pts2units(x1 + w2), pts2units(y1), pts2units(h), |
| col); |
| x1 += 2 * w2; |
| } |
| } else { |
| float h2 = 2 * w; |
| while (y1 + h2 < y2) { |
| getAFPDataStream().createLine(pts2units(x1), pts2units(y1), |
| pts2units(x1), pts2units(y1 + h2), pts2units(w), |
| col); |
| y1 += 2 * h2; |
| } |
| } |
| break; |
| case Constants.EN_DOTTED: |
| if (horz) { |
| while (x1 + h < x2) { |
| getAFPDataStream() |
| .createLine(pts2units(x1), pts2units(y1), |
| pts2units(x1 + h), pts2units(y1), |
| pts2units(h), col); |
| x1 += 2 * h; |
| } |
| } else { |
| while (y1 + w < y2) { |
| getAFPDataStream() |
| .createLine(pts2units(x1), pts2units(y1), |
| pts2units(x1), pts2units(y1 + w), |
| pts2units(w), col); |
| y1 += 2 * w; |
| } |
| } |
| break; |
| case Constants.EN_GROOVE: |
| case Constants.EN_RIDGE: { |
| float colFactor = (style == EN_GROOVE ? 0.4f : -0.4f); |
| if (horz) { |
| Color uppercol = lightenColor(col, -colFactor); |
| Color lowercol = lightenColor(col, colFactor); |
| float h3 = h / 3; |
| float ym1 = y1; |
| getAFPDataStream().createLine(pts2units(x1), pts2units(ym1), |
| pts2units(x2), pts2units(ym1), pts2units(h3), uppercol); |
| getAFPDataStream().createLine(pts2units(x1), pts2units(ym1 + h3), |
| pts2units(x2), pts2units(ym1 + h3), pts2units(h3), col); |
| getAFPDataStream().createLine(pts2units(x1), |
| pts2units(ym1 + h3 + h3), pts2units(x2), pts2units(ym1 |
| + h3 + h3), pts2units(h3), lowercol); |
| } else { |
| Color leftcol = lightenColor(col, -colFactor); |
| Color rightcol = lightenColor(col, colFactor); |
| float w3 = w / 3; |
| float xm1 = x1 + (w3 / 2); |
| getAFPDataStream().createLine(pts2units(xm1), pts2units(y1), |
| pts2units(xm1), pts2units(y2), pts2units(w3), leftcol); |
| getAFPDataStream().createLine(pts2units(xm1 + w3), pts2units(y1), |
| pts2units(xm1 + w3), pts2units(y2), pts2units(w3), col); |
| getAFPDataStream().createLine(pts2units(xm1 + w3 + w3), |
| pts2units(y1), pts2units(xm1 + w3 + w3), pts2units(y2), |
| pts2units(w3), rightcol); |
| } |
| break; |
| } |
| case Constants.EN_HIDDEN: |
| break; |
| case Constants.EN_INSET: |
| case Constants.EN_OUTSET: |
| default: |
| getAFPDataStream().createLine(pts2units(x1), pts2units(y1), |
| pts2units(horz ? x2 : x1), pts2units(horz ? y1 : y2), |
| pts2units(Math.abs(horz ? (y2 - y1) : (x2 - x1))), col); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| protected RendererContext createRendererContext(int x, int y, int width, |
| int height, Map foreignAttributes) { |
| RendererContext context; |
| context = super.createRendererContext(x, y, width, height, |
| foreignAttributes); |
| context.setProperty(AFPRendererContextConstants.AFP_GRAYSCALE, |
| new Boolean(!this.colorImages)); |
| context.setProperty(AFPRendererContextConstants.AFP_FONT_INFO, |
| this.fontInfo); |
| context.setProperty(AFPRendererContextConstants.AFP_RESOLUTION, |
| new Integer(this.resolution)); |
| context.setProperty(AFPRendererContextConstants.AFP_BITS_PER_PIXEL, |
| new Integer(this.bitsPerPixel)); |
| context.setProperty(AFPRendererContextConstants.AFP_DATASTREAM, |
| getAFPDataStream()); |
| context.setProperty(AFPRendererContextConstants.AFP_STATE, |
| getState()); |
| return context; |
| } |
| |
| private static final ImageFlavor[] FLAVORS = new ImageFlavor[] |
| {ImageFlavor.RAW_CCITTFAX, |
| ImageFlavor.GRAPHICS2D, |
| ImageFlavor.BUFFERED_IMAGE, |
| ImageFlavor.RENDERED_IMAGE, |
| ImageFlavor.XML_DOM}; |
| |
| /** {@inheritDoc} */ |
| public void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) { |
| uri = URISpecification.getURL(uri); |
| Rectangle posInt = new Rectangle( |
| (int)pos.getX(), |
| (int)pos.getY(), |
| (int)pos.getWidth(), |
| (int)pos.getHeight()); |
| Point origin = new Point(currentIPPosition, currentBPPosition); |
| int x = origin.x + posInt.x; |
| int y = origin.y + posInt.y; |
| |
| String name = null; |
| if (pageSegmentsMap != null) { |
| name = (String) pageSegmentsMap.get(uri); |
| } |
| if (name != null) { |
| getAFPDataStream().createIncludePageSegment(name, mpts2units(x), mpts2units(y)); |
| } else { |
| ImageManager manager = getUserAgent().getFactory().getImageManager(); |
| ImageInfo info = null; |
| try { |
| ImageSessionContext sessionContext = getUserAgent().getImageSessionContext(); |
| info = manager.getImageInfo(uri, sessionContext); |
| |
| //Only now fully load/prepare the image |
| Map hints = ImageUtil.getDefaultHints(sessionContext); |
| org.apache.xmlgraphics.image.loader.Image img = manager.getImage( |
| info, FLAVORS, hints, sessionContext); |
| |
| //...and process the image |
| if (img instanceof ImageGraphics2D) { |
| ImageGraphics2D imageG2D = (ImageGraphics2D)img; |
| RendererContext context = createRendererContext( |
| posInt.x, posInt.y, |
| posInt.width, posInt.height, foreignAttributes); |
| getGraphics2DAdapter().paintImage(imageG2D.getGraphics2DImagePainter(), |
| context, |
| origin.x + posInt.x, origin.y + posInt.y, |
| posInt.width, posInt.height); |
| } else if (img instanceof ImageRendered) { |
| ImageRendered imgRend = (ImageRendered)img; |
| RenderedImage ri = imgRend.getRenderedImage(); |
| |
| drawBufferedImage(ri, getResolution(), |
| posInt.x + currentIPPosition, |
| posInt.y + currentBPPosition, |
| posInt.width, |
| posInt.height); |
| } else if (img instanceof ImageRawCCITTFax) { |
| ImageRawCCITTFax ccitt = (ImageRawCCITTFax)img; |
| int afpx = mpts2units(posInt.x + currentIPPosition); |
| int afpy = mpts2units(posInt.y + currentBPPosition); |
| int afpw = mpts2units(posInt.getWidth()); |
| int afph = mpts2units(posInt.getHeight()); |
| int afpres = getResolution(); |
| ImageObject io = getAFPDataStream().getImageObject(afpx, afpy, afpw, afph, |
| afpres, afpres); |
| io.setImageParameters( |
| (int) (ccitt.getSize().getDpiHorizontal() * 10), |
| (int) (ccitt.getSize().getDpiVertical() * 10), |
| ccitt.getSize().getWidthPx(), |
| ccitt.getSize().getHeightPx()); |
| int compression = ccitt.getCompression(); |
| switch (compression) { |
| case TIFFImage.COMP_FAX_G3_1D : |
| io.setImageEncoding((byte) 0x80); |
| break; |
| case TIFFImage.COMP_FAX_G3_2D : |
| io.setImageEncoding((byte) 0x81); |
| break; |
| case TIFFImage.COMP_FAX_G4_2D : |
| io.setImageEncoding((byte) 0x82); |
| break; |
| default: |
| throw new IllegalStateException( |
| "Invalid compression scheme: " + compression); |
| } |
| InputStream in = ccitt.createInputStream(); |
| try { |
| byte[] buf = IOUtils.toByteArray(in); |
| io.setImageData(buf); |
| } finally { |
| IOUtils.closeQuietly(in); |
| } |
| } else if (img instanceof ImageXMLDOM) { |
| ImageXMLDOM imgXML = (ImageXMLDOM)img; |
| renderDocument(imgXML.getDocument(), imgXML.getRootNamespace(), |
| pos, foreignAttributes); |
| } else { |
| throw new UnsupportedOperationException("Unsupported image type: " + img); |
| } |
| |
| } catch (ImageException ie) { |
| log.error("Error while processing image: " |
| + (info != null ? info.toString() : uri), ie); |
| } catch (FileNotFoundException fe) { |
| log.error(fe.getMessage()); |
| } catch (IOException ioe) { |
| log.error("I/O error while processing image: " |
| + (info != null ? info.toString() : uri), ioe); |
| } |
| |
| /* |
| 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) || MimeConstants.MIME_SVG.equals(mime)) { |
| if (!fopimage.load(FopImage.ORIGINAL_DATA)) { |
| return; |
| } |
| Document doc = ((XMLImage) fopimage).getDocument(); |
| String ns = ((XMLImage) fopimage).getNameSpace(); |
| |
| renderDocument(doc, ns, pos, foreignAttributes); |
| } else if (MimeConstants.MIME_EPS.equals(mime)) { |
| log.warn("EPS images are not supported by this renderer"); |
| */ |
| /* |
| * } 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()); |
| int res = getResolution(); |
| ImageObject io = afpDataStream.getImageObject(x, y, w, h, res, |
| res); |
| 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(), |
| fopimage.getWidth(), fopimage.getHeight(), this.bitsPerPixel); |
| } |
| } else { |
| if (!fopimage.load(FopImage.BITMAP)) { |
| return; |
| } |
| convertToGrayScaleImage(io, fopimage.getBitmaps(), fopimage |
| .getWidth(), fopimage.getHeight(), this.bitsPerPixel); |
| } |
| } 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()); |
| int res = getResolution(); |
| ImageObject io = afpDataStream.getImageObject(x, y, w, h, res, |
| res); |
| 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(), fopimage |
| .getWidth(), fopimage.getHeight(), this.bitsPerPixel); |
| } |
| }*/ |
| } |
| } |
| |
| /** |
| * Writes a RenderedImage to an OutputStream as raw sRGB bitmaps. |
| * |
| * @param image |
| * the RenderedImage |
| * @param out |
| * the OutputStream |
| * @throws IOException |
| * In case of an I/O error. |
| */ |
| public static void writeImage(RenderedImage image, OutputStream out) |
| throws IOException { |
| ImageEncodingHelper.encodeRenderedImageAsRGB(image, out); |
| } |
| |
| /** |
| * Draws a BufferedImage to AFP. |
| * |
| * @param image |
| * the RenderedImage |
| * @param imageResolution |
| * the resolution of the BufferedImage |
| * @param x |
| * the x coordinate (in mpt) |
| * @param y |
| * the y coordinate (in mpt) |
| * @param w |
| * the width of the viewport (in mpt) |
| * @param h |
| * the height of the viewport (in mpt) |
| */ |
| public void drawBufferedImage(RenderedImage image, int imageResolution, int x, |
| int y, int w, int h) { |
| int afpx = mpts2units(x); |
| int afpy = mpts2units(y); |
| int afpw = mpts2units(w); |
| int afph = mpts2units(h); |
| int afpres = getResolution(); |
| ByteArrayOutputStream baout = new ByteArrayOutputStream(); |
| try { |
| // Serialize image |
| //TODO Eventually, this should be changed not to buffer as this increases the |
| //memory consumption (see PostScript output) |
| writeImage(image, baout); |
| byte[] buf = baout.toByteArray(); |
| |
| // Generate image |
| ImageObject io = getAFPDataStream().getImageObject(afpx, afpy, afpw, |
| afph, afpres, afpres); |
| io.setImageParameters(imageResolution, imageResolution, |
| image.getWidth(), image.getHeight()); |
| if (colorImages) { |
| io.setImageIDESize((byte)24); |
| io.setImageData(buf); |
| } else { |
| // TODO Teach it how to handle grayscale BufferedImages directly |
| // because this is pretty inefficient |
| convertToGrayScaleImage(io, buf, |
| image.getWidth(), image.getHeight(), this.bitsPerPixel); |
| } |
| } catch (IOException ioe) { |
| log.error("Error while serializing bitmap: " + ioe.getMessage(), |
| ioe); |
| } |
| } |
| |
| /** |
| * Establishes a new foreground or fill color. {@inheritDoc} |
| */ |
| public void updateColor(Color col, boolean fill) { |
| if (fill) { |
| getState().setColor(col); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public void restoreStateStackAfterBreakOut(List breakOutList) { |
| AbstractState.AbstractData data; |
| Iterator i = breakOutList.iterator(); |
| while (i.hasNext()) { |
| data = (AbstractState.AbstractData)i.next(); |
| saveGraphicsState(); |
| concatenateTransformationMatrix(data.getTransform()); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| protected List breakOutOfStateStack() { |
| List breakOutList = new java.util.ArrayList(); |
| AbstractState.AbstractData data; |
| while (true) { |
| data = currentState.getData(); |
| if (currentState.pop() == null) { |
| break; |
| } |
| breakOutList.add(0, data); //Insert because of stack-popping |
| } |
| return breakOutList; |
| } |
| |
| /** {@inheritDoc} */ |
| public void saveGraphicsState() { |
| getState().push(); |
| } |
| |
| /** {@inheritDoc} */ |
| public void restoreGraphicsState() { |
| getState().pop(); |
| } |
| |
| /** {@inheritDoc} */ |
| public void beginTextObject() { |
| } |
| |
| /** {@inheritDoc} */ |
| public void endTextObject() { |
| } |
| |
| /** {@inheritDoc} */ |
| public void renderImage(Image image, Rectangle2D pos) { |
| String url = image.getURL(); |
| drawImage(url, pos); |
| } |
| |
| // protected java.awt.geom.Point2D getInlineAreaPoint(InlineArea area) { |
| // return getState().getTransform().transform(super.getInlineAreaPoint(area), null); |
| // } |
| |
| /** {@inheritDoc} */ |
| public void renderText(TextArea text) { |
| renderInlineAreaBackAndBorders(text); |
| |
| //TODO: remove internal state handling of PresentationText objects and plug in AFPState |
| String internalFontName = getInternalFontNameForArea(text); |
| getState().setFontName(internalFontName); |
| |
| int currentFontSize = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue(); |
| getState().setFontSize(currentFontSize); |
| |
| AFPFont font = (AFPFont) fontInfo.getFonts().get(internalFontName); |
| |
| Color col = (Color) text.getTrait(Trait.COLOR); |
| getState().setColor(col); |
| |
| int variableSpaceCharacterIncrement = mpts2units(font.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(); |
| AFPFontAttributes afpFontAttributes = getState().getPageFonts().registerFont( |
| internalFontName, font, currentFontSize); |
| // Try and get the encoding to use for the font |
| String encoding = null; |
| try { |
| encoding = font.getCharacterSet(currentFontSize).getEncoding(); |
| } catch (Throwable ex) { |
| encoding = AFPConstants.EBCIDIC_ENCODING; |
| log.warn("renderText():: Error getting encoding for font " |
| + " - using default encoding " + encoding); |
| } |
| try { |
| getAFPDataStream().createText(afpFontAttributes.getFontReference(), |
| mpts2units(rx), pts2units(bl), |
| col, variableSpaceCharacterIncrement, |
| mpts2units(text.getTextLetterSpaceAdjust()), |
| worddata.getBytes(encoding)); |
| } catch (UnsupportedEncodingException usee) { |
| log.error("renderText:: Font " + afpFontAttributes.getFontKey() |
| + " caused UnsupportedEncodingException"); |
| } |
| super.renderText(text); |
| renderTextDecoration(font, currentFontSize, text, bl, rx); |
| } |
| |
| /** {@inheritDoc} */ |
| 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; |
| Color col = (Color) 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); |
| } |
| |
| /** |
| * 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 pageViewport |
| * the page object |
| */ |
| private void renderPageObjectExtensions(PageViewport pageViewport) { |
| pageSegmentsMap = null; |
| if (pageViewport.getExtensionAttachments() != null |
| && pageViewport.getExtensionAttachments().size() > 0) { |
| // Extract all AFPPageSetup instances from the attachment list on |
| // the s-p-m |
| Iterator i = pageViewport.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) { |
| getAFPDataStream().createIncludePageOverlay(overlay); |
| } |
| } else if (AFPElementMapping.INCLUDE_PAGE_SEGMENT |
| .equals(element)) { |
| String name = aps.getName(); |
| String source = aps.getValue(); |
| if (pageSegmentsMap == null) { |
| pageSegmentsMap = new java.util.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 java.util.HashMap(); |
| } |
| getAFPDataStream().createTagLogicalElement(name, value); |
| } else if (AFPElementMapping.NO_OPERATION.equals(element)) { |
| String content = aps.getContent(); |
| if (content != null) { |
| getAFPDataStream().createNoOperation(content); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * 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 |
| * @return afp measurement unit value |
| */ |
| protected int mpts2units(double mpt) { |
| return (int)Math.round(mpt / (DPI_CONVERSION_FACTOR / getResolution())); |
| } |
| |
| /** |
| * Converts a byte array containing 24 bit RGB image data to a grayscale |
| * image. |
| * |
| * @param io |
| * the target image object |
| * @param raw |
| * the buffer containing the RGB image data |
| * @param width |
| * the width of the image in pixels |
| * @param height |
| * the height of the image in pixels |
| * @param bitsPerPixel |
| * the number of bits to use per pixel |
| */ |
| protected static void convertToGrayScaleImage(ImageObject io, byte[] raw, int width, |
| int height, int bitsPerPixel) { |
| int pixelsPerByte = 8 / bitsPerPixel; |
| int bytewidth = (width / pixelsPerByte); |
| if ((width % pixelsPerByte) != 0) { |
| bytewidth++; |
| } |
| byte[] bw = new byte[height * bytewidth]; |
| byte ib; |
| for (int y = 0; y < height; y++) { |
| ib = 0; |
| int i = 3 * y * width; |
| for (int x = 0; x < width; x++, i += 3) { |
| |
| // 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) { |
| ib |= (byte) (1 << (7 - (x % 8))); |
| } |
| break; |
| case 4: |
| greyVal /= 16; |
| ib |= (byte) ((byte) greyVal << ((1 - (x % 2)) * 4)); |
| break; |
| case 8: |
| ib = (byte) greyVal; |
| break; |
| default: |
| throw new UnsupportedOperationException( |
| "Unsupported bits per pixel: " + bitsPerPixel); |
| } |
| |
| if ((x % pixelsPerByte) == (pixelsPerByte - 1) |
| || ((x + 1) == width)) { |
| bw[(y * bytewidth) + (x / pixelsPerByte)] = ib; |
| ib = 0; |
| } |
| } |
| } |
| io.setImageIDESize((byte) bitsPerPixel); |
| io.setImageData(bw); |
| } |
| |
| private Stack/*<ViewPortPos>*/ viewPortStack = null; |
| |
| private Stack/*<ViewPortPos>*/ getViewPortStack() { |
| if (viewPortStack == null) { |
| viewPortStack = new Stack/*<ViewPortPos>*/(); |
| } |
| return viewPortStack; |
| } |
| |
| private final class ViewPortPos { |
| private int x = 0; |
| |
| private int y = 0; |
| |
| private int rot = 0; |
| |
| private Rectangle2D view; |
| |
| ViewPortPos() { |
| } |
| |
| // private ViewPortPos(int x, int y, int rot) { |
| // this.x = x; |
| // this.y = y; |
| // this.rot = rot; |
| // } |
| // |
| |
| private ViewPortPos(Rectangle2D view, CTM ctm) { |
| this.view = view; |
| ViewPortPos currentVP = (ViewPortPos)getViewPortStack().peek(); |
| int xOrigin; |
| int yOrigin; |
| int width; |
| int height; |
| switch (currentVP.rot) { |
| case 90: |
| width = mpts2units(view.getHeight()); |
| height = mpts2units(view.getWidth()); |
| xOrigin = currentPageWidth - width - mpts2units(view.getY()) |
| - currentVP.y; |
| yOrigin = mpts2units(view.getX()) + currentVP.x; |
| break; |
| case 180: |
| width = mpts2units(view.getWidth()); |
| height = mpts2units(view.getHeight()); |
| xOrigin = currentPageWidth - width - mpts2units(view.getX()) |
| - currentVP.x; |
| yOrigin = currentPageHeight - height - mpts2units(view.getY()) |
| - currentVP.y; |
| break; |
| case 270: |
| width = mpts2units(view.getHeight()); |
| height = mpts2units(view.getWidth()); |
| xOrigin = mpts2units(view.getY()) + currentVP.y; |
| yOrigin = currentPageHeight - 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 = currentPageWidth - width - xOrigin; |
| break; |
| case 180: |
| this.x = currentPageWidth - width - xOrigin; |
| this.y = currentPageHeight - height - yOrigin; |
| break; |
| case 270: |
| this.x = currentPageHeight - height - yOrigin; |
| this.y = xOrigin; |
| break; |
| default: |
| } |
| } |
| |
| public String toString() { |
| return "x:" + x + " y:" + y + " rot:" + rot; |
| } |
| } |
| |
| /** |
| * Sets the number of bits used per pixel |
| * |
| * @param bitsPerPixel |
| * number of bits per pixel |
| */ |
| public void setBitsPerPixel(int bitsPerPixel) { |
| this.bitsPerPixel = bitsPerPixel; |
| 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; |
| } |
| } |
| |
| /** |
| * Sets whether images are color or not |
| * |
| * @param colorImages |
| * color image output |
| */ |
| public void setColorImages(boolean colorImages) { |
| this.colorImages = colorImages; |
| } |
| |
| /** |
| * Returns the AFPDataStream |
| * |
| * @return the AFPDataStream |
| */ |
| public AFPDataStream getAFPDataStream() { |
| if (afpDataStream == null) { |
| this.afpDataStream = new AFPDataStream(); |
| } |
| return afpDataStream; |
| } |
| |
| /** |
| * Sets the output/device resolution |
| * |
| * @param resolution |
| * the output resolution (dpi) |
| */ |
| public void setResolution(int resolution) { |
| if (resolution == DPI_240_RESOLUTION |
| || resolution == DPI_1440_RESOLUTION) { |
| this.resolution = resolution; |
| if (log.isDebugEnabled()) { |
| log.debug("renderer-resolution set to: " + resolution + " dpi"); |
| } |
| } else { |
| log.error("invalid resolution, can only be " + DPI_240_RESOLUTION |
| + " or " + DPI_1440_RESOLUTION + " dpi"); |
| } |
| } |
| |
| /** |
| * Returns the output/device resolution. |
| * |
| * @return the resolution in dpi |
| */ |
| public int getResolution() { |
| return this.resolution; |
| } |
| |
| private boolean gocaEnabled = false; |
| |
| /** |
| * @param enabled true if AFP GOCA is enabled for SVG support |
| */ |
| protected void setGOCAEnabled(boolean enabled) { |
| this.gocaEnabled = enabled; |
| } |
| |
| /** |
| * @return true of AFP GOCA is enabled for SVG support |
| */ |
| protected boolean isGOCAEnabled() { |
| return this.gocaEnabled; |
| } |
| } |