/* * 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.afp.modca; import java.awt.Color; import java.io.IOException; import java.io.OutputStream; import java.util.List; import org.apache.xmlgraphics.java2d.color.ColorConverter; import org.apache.xmlgraphics.java2d.color.ColorUtil; import org.apache.fop.afp.AFPDataObjectInfo; import org.apache.fop.afp.AFPObjectAreaInfo; import org.apache.fop.afp.Factory; import org.apache.fop.afp.StructuredData; import org.apache.fop.afp.fonts.CharacterSet; import org.apache.fop.afp.goca.GraphicsAreaBegin; import org.apache.fop.afp.goca.GraphicsAreaEnd; import org.apache.fop.afp.goca.GraphicsBox; import org.apache.fop.afp.goca.GraphicsChainedSegment; import org.apache.fop.afp.goca.GraphicsCharacterString; import org.apache.fop.afp.goca.GraphicsData; import org.apache.fop.afp.goca.GraphicsEndProlog; import org.apache.fop.afp.goca.GraphicsFillet; import org.apache.fop.afp.goca.GraphicsFullArc; import org.apache.fop.afp.goca.GraphicsImage; import org.apache.fop.afp.goca.GraphicsLine; import org.apache.fop.afp.goca.GraphicsSetArcParameters; import org.apache.fop.afp.goca.GraphicsSetCharacterSet; import org.apache.fop.afp.goca.GraphicsSetCurrentPosition; import org.apache.fop.afp.goca.GraphicsSetFractionalLineWidth; import org.apache.fop.afp.goca.GraphicsSetLineType; import org.apache.fop.afp.goca.GraphicsSetLineWidth; import org.apache.fop.afp.goca.GraphicsSetPatternSymbol; import org.apache.fop.afp.goca.GraphicsSetProcessColor; /** * Top-level GOCA graphics object. * * Acts as container and factory of all other graphic objects */ public class GraphicsObject extends AbstractDataObject { /** the graphics data */ private GraphicsData currentData; /** list of objects contained within this container */ protected List<GraphicsData> objects = new java.util.ArrayList<GraphicsData>(); /** the graphics state */ private final GraphicsState graphicsState = new GraphicsState(); /** color converter */ private ColorConverter colorConverter; /** * Default constructor * * @param factory the object factory * @param name the name of graphics object */ public GraphicsObject(Factory factory, String name) { super(factory, name); } /** {@inheritDoc} */ @Override public void setViewport(AFPDataObjectInfo dataObjectInfo) { super.setViewport(dataObjectInfo); AFPObjectAreaInfo objectAreaInfo = dataObjectInfo.getObjectAreaInfo(); int width = objectAreaInfo.getWidth(); int height = objectAreaInfo.getHeight(); int widthRes = objectAreaInfo.getWidthRes(); int heightRes = objectAreaInfo.getHeightRes(); final int leftEdge = 0; final int topEdge = 0; GraphicsDataDescriptor graphicsDataDescriptor = factory.createGraphicsDataDescriptor( leftEdge, width, topEdge, height, widthRes, heightRes); getObjectEnvironmentGroup().setDataDescriptor(graphicsDataDescriptor); } /** @param object the structured data */ public void addObject(StructuredData object) { if (currentData == null) { newData(); } else if (currentData.getDataLength() + object.getDataLength() >= GraphicsData.MAX_DATA_LEN) { // graphics data full so transfer current incomplete segment to new data GraphicsChainedSegment currentSegment = (GraphicsChainedSegment)currentData.removeCurrentSegment(); currentSegment.setName(newData().createSegmentName()); currentData.addSegment(currentSegment); } currentData.addObject(object); } /** * Gets the current graphics data, creating a new one if necessary * * @return the current graphics data */ private GraphicsData getData() { if (this.currentData == null) { return newData(); } return this.currentData; } /** * Creates a new graphics data * * @return a newly created graphics data */ private GraphicsData newData() { if (currentData != null) { currentData.setComplete(true); } this.currentData = factory.createGraphicsData(); objects.add(currentData); return currentData; } /** * Sets the current color * * @param color the active color to use */ public void setColor(Color color) { if (!ColorUtil.isSameColor(color, graphicsState.color)) { addObject(new GraphicsSetProcessColor(colorConverter.convert(color))); graphicsState.color = color; } } /** * Sets the color converter * * @param colorConverter ColorConverter to filter the color * when creating a GraphicsSetProcessColor. */ public void setColorConverter(ColorConverter colorConverter) { this.colorConverter = colorConverter; } /** * Sets the current position * * @param coords the x and y coordinates of the current position */ public void setCurrentPosition(int[] coords) { addObject(new GraphicsSetCurrentPosition(coords)); } /** * Sets the line width * * @param lineWidth the line width multiplier */ public void setLineWidth(int lineWidth) { if ((float) lineWidth != graphicsState.lineWidth) { addObject(new GraphicsSetLineWidth(lineWidth)); graphicsState.lineWidth = (float) lineWidth; } } /** * Sets the line width * * @param lineWidth the line width multiplier */ public void setLineWidth(float lineWidth) { float epsilon = Float.intBitsToFloat(0x00800000); // Float.MIN_NORMAL (JDK1.6) if (Math.abs(graphicsState.lineWidth - lineWidth) > epsilon) { addObject(new GraphicsSetFractionalLineWidth(lineWidth)); graphicsState.lineWidth = lineWidth; } } /** * Sets the line type * * @param lineType the line type */ public void setLineType(byte lineType) { if (lineType != graphicsState.lineType) { addObject(new GraphicsSetLineType(lineType)); graphicsState.lineType = lineType; } } /** * Sets whether the following shape is to be filled. * * @param fill true if the following shape is to be filled */ public void setFill(boolean fill) { setPatternSymbol(fill ? GraphicsSetPatternSymbol.SOLID_FILL : GraphicsSetPatternSymbol.NO_FILL); } /** * Sets the fill pattern of the next shape. * * @param patternSymbol the fill pattern of the next shape */ public void setPatternSymbol(byte patternSymbol) { if (patternSymbol != graphicsState.patternSymbol) { addObject(new GraphicsSetPatternSymbol(patternSymbol)); graphicsState.patternSymbol = patternSymbol; } } /** * Sets the character set to use * * @param characterSet the character set (font) reference */ public void setCharacterSet(int characterSet) { if (characterSet != graphicsState.characterSet) { graphicsState.characterSet = characterSet; } addObject(new GraphicsSetCharacterSet(characterSet)); } /** * Adds a line at the given x/y coordinates * * @param coords the x/y coordinates (can be a series) */ public void addLine(int[] coords) { addLine(coords, false); } /** * Adds a line at the given x/y coordinates * * @param coords the x/y coordinates (can be a series) * @param relative relative true for a line at current position (relative to) */ public void addLine(int[] coords, boolean relative) { addObject(new GraphicsLine(coords, relative)); } /** * Adds a box at the given coordinates * * @param coords the x/y coordinates */ public void addBox(int[] coords) { addObject(new GraphicsBox(coords)); } /** * Adds a fillet (curve) at the given coordinates * * @param coords the x/y coordinates */ public void addFillet(int[] coords) { addFillet(coords, false); } /** * Adds a fillet (curve) at the given coordinates * * @param coords the x/y coordinates * @param relative relative true for a fillet (curve) at current position (relative to) */ public void addFillet(int[] coords, boolean relative) { addObject(new GraphicsFillet(coords, relative)); } /** * Sets the arc parameters * * @param xmaj the maximum value of the x coordinate * @param ymin the minimum value of the y coordinate * @param xmin the minimum value of the x coordinate * @param ymaj the maximum value of the y coordinate */ public void setArcParams(int xmaj, int ymin, int xmin, int ymaj) { addObject(new GraphicsSetArcParameters(xmaj, ymin, xmin, ymaj)); } /** * Adds a full arc * * @param x the x coordinate * @param y the y coordinate * @param mh the integer portion of the multiplier * @param mhr the fractional portion of the multiplier */ public void addFullArc(int x, int y, int mh, int mhr) { addObject(new GraphicsFullArc(x, y, mh, mhr)); } /** * Adds an image * * @param x the x coordinate * @param y the y coordinate * @param width the image width * @param height the image height * @param imgData the image data */ public void addImage(int x, int y, int width, int height, byte[] imgData) { addObject(new GraphicsImage(x, y, width, height, imgData)); } /** * Adds a string * * @param str the string * @param x the x coordinate * @param y the y coordinate * @param charSet the character set associated with the string */ public void addString(String str, int x, int y, CharacterSet charSet) { addObject(new GraphicsCharacterString(str, x, y, charSet)); } /** * Begins a graphics area (start of fill) */ public void beginArea() { addObject(new GraphicsAreaBegin()); } /** * Ends a graphics area (end of fill) */ public void endArea() { addObject(new GraphicsAreaEnd()); } /** * Ends the prolog. */ public void endProlog() { addObject(new GraphicsEndProlog()); } /** {@inheritDoc} */ @Override public String toString() { return "GraphicsObject: " + getName(); } /** * Creates a new graphics segment */ public void newSegment() { getData().newSegment(); graphicsState.lineWidth = 0; //Looks like a new segment invalidates the graphics state } /** {@inheritDoc} */ @Override public void setComplete(boolean complete) { for (GraphicsData completedObject : objects) { completedObject.setComplete(true); } super.setComplete(complete); } /** {@inheritDoc} */ @Override protected void writeStart(OutputStream os) throws IOException { super.writeStart(os); byte[] data = new byte[17]; copySF(data, Type.BEGIN, Category.GRAPHICS); os.write(data); } /** {@inheritDoc} */ @Override protected void writeContent(OutputStream os) throws IOException { super.writeContent(os); writeObjects(objects, os); } /** {@inheritDoc} */ @Override protected void writeEnd(OutputStream os) throws IOException { byte[] data = new byte[17]; copySF(data, Type.END, Category.GRAPHICS); os.write(data); } /** the internal graphics state */ private static final class GraphicsState { private GraphicsState() { } /** the current color */ private Color color; /** the current line type */ private byte lineType; /** the current line width */ private float lineWidth; /** the current fill pattern */ private byte patternSymbol; /** the current character set */ private int characterSet; } }