001: /***********************************************************************************************
002: * Copyright 2002 (C) Nathaniel G. Auvil. All Rights Reserved.
003: *
004: * Redistribution and use of this software and associated documentation ("Software"), with or
005: * without modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain copyright statements and notices.
008: * Redistributions must also contain a copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
011: * conditions and the following disclaimer in the documentation and/or other materials
012: * provided with the distribution.
013: *
014: * 3. The name "jCharts" or "Nathaniel G. Auvil" must not be used to endorse or promote
015: * products derived from this Software without prior written permission of Nathaniel G.
016: * Auvil. For written permission, please contact nathaniel_auvil@users.sourceforge.net
017: *
018: * 4. Products derived from this Software may not be called "jCharts" nor may "jCharts" appear
019: * in their names without prior written permission of Nathaniel G. Auvil. jCharts is a
020: * registered trademark of Nathaniel G. Auvil.
021: *
022: * 5. Due credit should be given to the jCharts Project (http://jcharts.sourceforge.net/).
023: *
024: * THIS SOFTWARE IS PROVIDED BY Nathaniel G. Auvil AND CONTRIBUTORS ``AS IS'' AND ANY
025: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
026: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
027: * jCharts OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
028: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
029: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
030: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,STRICT LIABILITY, OR TORT
031: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
032: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
033: ************************************************************************************************/package org.krysalis.jcharts;
034:
035: import org.krysalis.jcharts.chartData.ChartDataException;
036: import org.krysalis.jcharts.imageMap.ImageMap;
037: import org.krysalis.jcharts.properties.ChartProperties;
038: import org.krysalis.jcharts.properties.LegendProperties;
039: import org.krysalis.jcharts.properties.PropertyException;
040: import org.krysalis.jcharts.test.HTMLGenerator;
041:
042: import java.awt.*;
043: import java.awt.font.FontRenderContext;
044: import java.awt.font.LineBreakMeasurer;
045: import java.awt.font.TextAttribute;
046: import java.awt.font.TextLayout;
047: import java.awt.geom.Rectangle2D;
048: import java.awt.image.BufferedImage;
049: import java.io.Serializable;
050: import java.text.AttributedString;
051:
052: /*************************************************************************************
053: * Base class of all charts.
054: *
055: * @author Nathaniel Auvil, Sandor Dornbush
056: * @version $Id: Chart.java,v 1.3 2003/06/21 19:44:35 nathaniel_auvil Exp $
057: ************************************************************************************/
058: public abstract class Chart implements Serializable {
059: //---Be aware that the calls BufferedImage.getGraphics() and BufferedImage.createGraphics(), actually
060: //--- create a new Graphics2D Object on each invocation. So keep a reference here.
061: private Graphics2D graphics2D;
062:
063: private int width;
064: private int height;
065:
066: private Legend legend;
067:
068: //---general properties of all chart types.
069: private ChartProperties chartProperties;
070:
071: //---used to generate an image map for the image
072: private boolean generateImageMap = false;
073: private ImageMap imageMap = null;
074: private BufferedImage bufferedImage = null;
075:
076: /******************************************************************************************
077: * Constructor
078: *
079: * @param legendProperties
080: * @param chartProperties
081: * @param pixelWidth
082: * @param pixelHeight
083: *******************************************************************************************/
084: public Chart(LegendProperties legendProperties,
085: ChartProperties chartProperties, int pixelWidth,
086: int pixelHeight) {
087: this .width = pixelWidth;
088: this .height = pixelHeight;
089: this .chartProperties = chartProperties;
090:
091: if (legendProperties != null) {
092: this .legend = new Legend(this , legendProperties);
093: legendProperties.setSize(new Dimension(width, height));
094: }
095: }
096:
097: /******************************************************************************************
098: * Returns flag indicating whether to generate an ImageMap
099: *
100: * @return boolean
101: *******************************************************************************************/
102: public boolean getGenerateImageMapFlag() {
103: return this .generateImageMap;
104: }
105:
106: /******************************************************************************************
107: * Returns the BufferedImage used to generate the ImageMap. Only should be called on
108: * binary format images, such as PNG and JPG, as it will not work on SVG.
109: *
110: * This is a HACK and the design of jCharts should do better than this!!!!!!
111: *
112: * @return BufferedImage
113: *******************************************************************************************/
114: public BufferedImage getBufferedImage() {
115: return this .bufferedImage;
116: }
117:
118: /******************************************************************************************
119: * Renders the chart into a BufferedImage so that we can calculate all the Image Map
120: * coordinates.
121: *
122: * @throws ChartDataException
123: * @throws PropertyException
124: *******************************************************************************************/
125: public void renderWithImageMap() throws ChartDataException,
126: PropertyException {
127: this .bufferedImage = new BufferedImage(this .getImageWidth(),
128: this .getImageHeight(), BufferedImage.TYPE_INT_RGB);
129: this .setGraphics2D(this .bufferedImage.createGraphics());
130: this .generateImageMap = true;
131:
132: this .render();
133: }
134:
135: /******************************************************************************************
136: * Call this to kick off rendering of the chart
137: *
138: * @throws ChartDataException
139: * @throws PropertyException
140: *******************************************************************************************/
141: public void render() throws ChartDataException, PropertyException {
142: if (this .chartProperties.useAntiAliasing()) {
143: this .graphics2D.setRenderingHint(
144: RenderingHints.KEY_ANTIALIASING,
145: RenderingHints.VALUE_ANTIALIAS_ON);
146: } else {
147: this .graphics2D.setRenderingHint(
148: RenderingHints.KEY_ANTIALIASING,
149: RenderingHints.VALUE_ANTIALIAS_OFF);
150: }
151:
152: Rectangle2D.Float rectangle = new Rectangle2D.Float(0, 0,
153: this .width, this .height);
154:
155: //---fill the background
156: this .graphics2D.setPaint(this .chartProperties
157: .getBackgroundPaint());
158: this .graphics2D.fill(rectangle);
159:
160: //---draw a border around the chart if desired
161: if (this .chartProperties.getBorderStroke() != null) {
162: //---there is a one pixel difference when you fill versus draw a rectangle
163: rectangle.width -= 1;
164: rectangle.height -= 1;
165: this .chartProperties.getBorderStroke().draw(
166: this .graphics2D, rectangle);
167: }
168:
169: //---draw the chart
170: this .renderChart();
171: }
172:
173: /*************************************************************************************************
174: * Displays the chart title and returns the height of the title PLUS title padding.
175: *
176: * @param chartTitle
177: * @param fontRenderContext
178: * @return float the height required by the title. If no title is displayed, zero is returned.
179: **************************************************************************************************/
180: protected float renderChartTitle(String chartTitle,
181: FontRenderContext fontRenderContext) {
182: float height = 0;
183:
184: if (chartTitle != null) {
185: //---the y value of where to write the current line
186: float currentLine = this .getChartProperties()
187: .getEdgePadding();
188:
189: // change title into an AttributedString with the font as an attribute
190: AttributedString s = new AttributedString(chartTitle);
191: s.addAttribute(TextAttribute.FONT, this
192: .getChartProperties().getTitleFont().getFont());
193:
194: //---get LineBreakMeasurer on the attributed string...it will break the text for us
195: LineBreakMeasurer measurer = new LineBreakMeasurer(s
196: .getIterator(), fontRenderContext);
197:
198: //---set the text color
199: this .getGraphics2D()
200: .setPaint(
201: this .getChartProperties().getTitleFont()
202: .getPaint());
203:
204: //---draw each title line. Subtract padding from each side for printable width.
205: float wrappingWidth = this .getImageWidth()
206: - (this .getChartProperties().getEdgePadding() * 2);
207:
208: TextLayout titleTextLayout = null;
209: while ((titleTextLayout = measurer
210: .nextLayout(wrappingWidth)) != null) {
211: //---set the current line to where the title will be written
212: currentLine += titleTextLayout.getAscent();
213:
214: titleTextLayout.draw(this .getGraphics2D(),
215: ((this .getImageWidth() - titleTextLayout
216: .getAdvance()) / 2), currentLine);
217:
218: //---keep track of total height of all the title lines
219: height += titleTextLayout.getAscent()
220: + titleTextLayout.getDescent();
221: }
222:
223: //---add in the padding between the title and the top of the chart.
224: height += this .getChartProperties().getTitlePadding();
225: }
226:
227: return height;
228: }
229:
230: /******************************************************************************************
231: *
232: * @throws ChartDataException
233: * @throws PropertyException
234: *******************************************************************************************/
235: abstract protected void renderChart() throws ChartDataException,
236: PropertyException;
237:
238: /******************************************************************************************
239: * Returns the BufferedImage width
240: *
241: * @return int
242: *******************************************************************************************/
243: public final int getImageWidth() {
244: return this .width;
245: }
246:
247: /******************************************************************************************
248: * Returns the BufferedImage height
249: *
250: * @return int
251: *******************************************************************************************/
252: public final int getImageHeight() {
253: return this .height;
254: }
255:
256: /******************************************************************************************
257: * Returns the general properties Object.
258: *
259: * @return ChartProperties
260: *******************************************************************************************/
261: public final ChartProperties getChartProperties() {
262: return this .chartProperties;
263: }
264:
265: /******************************************************************************************
266: * Returns the Legend. Will be NULL if no Legend is desired.
267: *
268: * @return Legend
269: *******************************************************************************************/
270: protected final Legend getLegend() {
271: return this .legend;
272: }
273:
274: /******************************************************************************************
275: * Returns flag indicating if there is a Legend.
276: *
277: * @return boolean
278: *******************************************************************************************/
279: public final boolean hasLegend() {
280: return this .legend != null;
281: }
282:
283: /******************************************************************************************
284: * Sets the graphics object to render the chart on by the encoder.
285: *
286: * @param graphics2D
287: *******************************************************************************************/
288: public final void setGraphics2D(Graphics2D graphics2D) {
289: this .graphics2D = graphics2D;
290: }
291:
292: /******************************************************************************************
293: * Shortcut method to get Graphics2D. Be aware that the call BufferedImage.getGraphics()
294: * and BufferedImage.createGraphics(), actually create a new Grpahics2D Object on each
295: * invocation. This returns the member reference so calls to this are not creating a new
296: * Object each time.
297: *
298: * @return Graphics2D
299: *******************************************************************************************/
300: public final Graphics2D getGraphics2D() {
301: return this .graphics2D;
302: }
303:
304: /**********************************************************************************************
305: * To optimze performance of the ImageMap Object, we create it once we know how many data
306: * elements are in the chart which is dependent on the AxisChart or PieChart2D
307: *
308: * @param imageMap
309: **********************************************************************************************/
310: public final void setImageMap(ImageMap imageMap) {
311: this .imageMap = imageMap;
312: }
313:
314: /**********************************************************************************************
315: *
316: * @return imageMap
317: **********************************************************************************************/
318: public final ImageMap getImageMap() {
319: return this .imageMap;
320: }
321:
322: /**********************************************************************************************
323: * Enables the testing routines to display the contents of this Object.
324: *
325: * @param htmlGenerator
326: * @param imageFileName
327: * @param imageMap if this is NULL we are not creating image map data in html
328: **********************************************************************************************/
329: public void toHTML(HTMLGenerator htmlGenerator,
330: String imageFileName, ImageMap imageMap) {
331: htmlGenerator.chartTableStart(this.getClass().getName(),
332: imageFileName, imageMap);
333:
334: htmlGenerator.chartTableRowStart();
335: this.chartProperties.toHTML(htmlGenerator);
336: htmlGenerator.chartTableRowEnd();
337:
338: if (this.legend != null) {
339: htmlGenerator.chartTableRowStart();
340: this.getLegend().toHTML(htmlGenerator);
341: htmlGenerator.chartTableRowEnd();
342: }
343:
344: htmlGenerator.chartTableEnd();
345: }
346:
347: }
|