001: /* ====================================================================
002: Licensed to the Apache Software Foundation (ASF) under one or more
003: contributor license agreements. See the NOTICE file distributed with
004: this work for additional information regarding copyright ownership.
005: The ASF licenses this file to You under the Apache License, Version 2.0
006: (the "License"); you may not use this file except in compliance with
007: the License. You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016: ==================================================================== */
017:
018: package org.apache.poi.hssf.usermodel;
019:
020: import org.apache.poi.hssf.util.HSSFColor;
021: import org.apache.poi.util.POILogFactory;
022: import org.apache.poi.util.POILogger;
023:
024: import java.awt.*;
025: import java.awt.image.ImageObserver;
026: import java.text.AttributedCharacterIterator;
027:
028: /**
029: * Translates Graphics calls into escher calls. The translation is lossy so
030: * many features are not supported and some just aren't implemented yet. If
031: * in doubt test the specific calls you wish to make. Graphics calls are
032: * always performed into an EscherGroup so one will need to be created.
033: * <p>
034: * <b>Important:</b>
035: * <blockquote>
036: * One important concept worth considering is that of font size. One of the
037: * difficulties in converting Graphics calls into escher drawing calls is that
038: * Excel does not have the concept of absolute pixel positions. It measures
039: * it's cell widths in 'characters' and the cell heights in points.
040: * Unfortunately it's not defined exactly what a type of character it's
041: * measuring. Presumably this is due to the fact that the Excel will be
042: * using different fonts on different platforms or even within the same
043: * platform.
044: * <p>
045: * Because of this constraint we've had to calculate the
046: * verticalPointsPerPixel. This the amount the font should be scaled by when
047: * you issue commands such as drawString(). A good way to calculate this
048: * is to use the follow formula:
049: * <p>
050: * <pre>
051: * multipler = groupHeightInPoints / heightOfGroup
052: * </pre>
053: * <p>
054: * The height of the group is calculated fairly simply by calculating the
055: * difference between the y coordinates of the bounding box of the shape. The
056: * height of the group can be calculated by using a convenience called
057: * <code>HSSFClientAnchor.getAnchorHeightInPoints()</code>.
058: * </blockquote>
059: *
060: * @author Glen Stampoultzis (glens at apache.org)
061: */
062: public class EscherGraphics extends Graphics {
063: private HSSFShapeGroup escherGroup;
064: private HSSFWorkbook workbook;
065: private float verticalPointsPerPixel = 1.0f;
066: private float verticalPixelsPerPoint;
067: private Color foreground;
068: private Color background = Color.white;
069: private Font font;
070: private static POILogger logger = POILogFactory
071: .getLogger(EscherGraphics.class);
072:
073: /**
074: * Construct an escher graphics object.
075: *
076: * @param escherGroup The escher group to write the graphics calls into.
077: * @param workbook The workbook we are using.
078: * @param forecolor The foreground color to use as default.
079: * @param verticalPointsPerPixel The font multiplier. (See class description for information on how this works.).
080: */
081: public EscherGraphics(HSSFShapeGroup escherGroup,
082: HSSFWorkbook workbook, Color forecolor,
083: float verticalPointsPerPixel) {
084: this .escherGroup = escherGroup;
085: this .workbook = workbook;
086: this .verticalPointsPerPixel = verticalPointsPerPixel;
087: this .verticalPixelsPerPoint = 1 / verticalPointsPerPixel;
088: this .font = new Font("Arial", 0, 10);
089: this .foreground = forecolor;
090: // background = backcolor;
091: }
092:
093: /**
094: * Constructs an escher graphics object.
095: *
096: * @param escherGroup The escher group to write the graphics calls into.
097: * @param workbook The workbook we are using.
098: * @param foreground The foreground color to use as default.
099: * @param verticalPointsPerPixel The font multiplier. (See class description for information on how this works.).
100: * @param font The font to use.
101: */
102: EscherGraphics(HSSFShapeGroup escherGroup, HSSFWorkbook workbook,
103: Color foreground, Font font, float verticalPointsPerPixel) {
104: this .escherGroup = escherGroup;
105: this .workbook = workbook;
106: this .foreground = foreground;
107: // this.background = background;
108: this .font = font;
109: this .verticalPointsPerPixel = verticalPointsPerPixel;
110: this .verticalPixelsPerPoint = 1 / verticalPointsPerPixel;
111: }
112:
113: // /**
114: // * Constructs an escher graphics object.
115: // *
116: // * @param escherGroup The escher group to write the graphics calls into.
117: // * @param workbook The workbook we are using.
118: // * @param forecolor The default foreground color.
119: // */
120: // public EscherGraphics( HSSFShapeGroup escherGroup, HSSFWorkbook workbook, Color forecolor)
121: // {
122: // this(escherGroup, workbook, forecolor, 1.0f);
123: // }
124:
125: public void clearRect(int x, int y, int width, int height) {
126: Color color = foreground;
127: setColor(background);
128: fillRect(x, y, width, height);
129: setColor(color);
130: }
131:
132: public void clipRect(int x, int y, int width, int height) {
133: if (logger.check(POILogger.WARN))
134: logger.log(POILogger.WARN, "clipRect not supported");
135: }
136:
137: public void copyArea(int x, int y, int width, int height, int dx,
138: int dy) {
139: if (logger.check(POILogger.WARN))
140: logger.log(POILogger.WARN, "copyArea not supported");
141: }
142:
143: public Graphics create() {
144: EscherGraphics g = new EscherGraphics(escherGroup, workbook,
145: foreground, font, verticalPointsPerPixel);
146: return g;
147: }
148:
149: public void dispose() {
150: }
151:
152: public void drawArc(int x, int y, int width, int height,
153: int startAngle, int arcAngle) {
154: if (logger.check(POILogger.WARN))
155: logger.log(POILogger.WARN, "drawArc not supported");
156: }
157:
158: public boolean drawImage(Image img, int dx1, int dy1, int dx2,
159: int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor,
160: ImageObserver observer) {
161: if (logger.check(POILogger.WARN))
162: logger.log(POILogger.WARN, "drawImage not supported");
163:
164: return true;
165: }
166:
167: public boolean drawImage(Image img, int dx1, int dy1, int dx2,
168: int dy2, int sx1, int sy1, int sx2, int sy2,
169: ImageObserver observer) {
170: if (logger.check(POILogger.WARN))
171: logger.log(POILogger.WARN, "drawImage not supported");
172: return true;
173: }
174:
175: public boolean drawImage(Image image, int i, int j, int k, int l,
176: Color color, ImageObserver imageobserver) {
177: return drawImage(image, i, j, i + k, j + l, 0, 0, image
178: .getWidth(imageobserver), image
179: .getHeight(imageobserver), color, imageobserver);
180: }
181:
182: public boolean drawImage(Image image, int i, int j, int k, int l,
183: ImageObserver imageobserver) {
184: return drawImage(image, i, j, i + k, j + l, 0, 0, image
185: .getWidth(imageobserver), image
186: .getHeight(imageobserver), imageobserver);
187: }
188:
189: public boolean drawImage(Image image, int i, int j, Color color,
190: ImageObserver imageobserver) {
191: return drawImage(image, i, j, image.getWidth(imageobserver),
192: image.getHeight(imageobserver), color, imageobserver);
193: }
194:
195: public boolean drawImage(Image image, int i, int j,
196: ImageObserver imageobserver) {
197: return drawImage(image, i, j, image.getWidth(imageobserver),
198: image.getHeight(imageobserver), imageobserver);
199: }
200:
201: public void drawLine(int x1, int y1, int x2, int y2) {
202: drawLine(x1, y1, x2, y2, 0);
203: }
204:
205: public void drawLine(int x1, int y1, int x2, int y2, int width) {
206: HSSFSimpleShape shape = escherGroup
207: .createShape(new HSSFChildAnchor(x1, y1, x2, y2));
208: shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
209: shape.setLineWidth(width);
210: shape.setLineStyleColor(foreground.getRed(), foreground
211: .getGreen(), foreground.getBlue());
212: }
213:
214: public void drawOval(int x, int y, int width, int height) {
215: HSSFSimpleShape shape = escherGroup
216: .createShape(new HSSFChildAnchor(x, y, x + width, y
217: + height));
218: shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
219: shape.setLineWidth(0);
220: shape.setLineStyleColor(foreground.getRed(), foreground
221: .getGreen(), foreground.getBlue());
222: shape.setNoFill(true);
223: }
224:
225: public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {
226: int right = findBiggest(xPoints);
227: int bottom = findBiggest(yPoints);
228: int left = findSmallest(xPoints);
229: int top = findSmallest(yPoints);
230: HSSFPolygon shape = escherGroup
231: .createPolygon(new HSSFChildAnchor(left, top, right,
232: bottom));
233: shape.setPolygonDrawArea(right - left, bottom - top);
234: shape.setPoints(addToAll(xPoints, -left), addToAll(yPoints,
235: -top));
236: shape.setLineStyleColor(foreground.getRed(), foreground
237: .getGreen(), foreground.getBlue());
238: shape.setLineWidth(0);
239: shape.setNoFill(true);
240: }
241:
242: private int[] addToAll(int[] values, int amount) {
243: int[] result = new int[values.length];
244: for (int i = 0; i < values.length; i++)
245: result[i] = values[i] + amount;
246: return result;
247: }
248:
249: public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {
250: if (logger.check(POILogger.WARN))
251: logger.log(POILogger.WARN, "drawPolyline not supported");
252: }
253:
254: public void drawRect(int x, int y, int width, int height) {
255: if (logger.check(POILogger.WARN))
256: logger.log(POILogger.WARN, "drawRect not supported");
257: }
258:
259: public void drawRoundRect(int x, int y, int width, int height,
260: int arcWidth, int arcHeight) {
261: if (logger.check(POILogger.WARN))
262: logger.log(POILogger.WARN, "drawRoundRect not supported");
263: }
264:
265: public void drawString(String str, int x, int y) {
266: if (str == null || str.equals(""))
267: return;
268:
269: Font excelFont = font;
270: if (font.getName().equals("SansSerif")) {
271: excelFont = new Font("Arial", font.getStyle(), (int) (font
272: .getSize() / verticalPixelsPerPoint));
273: } else {
274: excelFont = new Font(font.getName(), font.getStyle(),
275: (int) (font.getSize() / verticalPixelsPerPoint));
276: }
277: FontDetails d = StaticFontMetrics.getFontDetails(excelFont);
278: int width = (int) ((d.getStringWidth(str) * 8) + 12);
279: int height = (int) ((font.getSize() / verticalPixelsPerPoint) + 6) * 2;
280: y -= (font.getSize() / verticalPixelsPerPoint) + 2
281: * verticalPixelsPerPoint; // we want to draw the shape from the top-left
282: HSSFTextbox textbox = escherGroup
283: .createTextbox(new HSSFChildAnchor(x, y, x + width, y
284: + height));
285: textbox.setNoFill(true);
286: textbox.setLineStyle(HSSFShape.LINESTYLE_NONE);
287: HSSFRichTextString s = new HSSFRichTextString(str);
288: HSSFFont hssfFont = matchFont(excelFont);
289: s.applyFont(hssfFont);
290: textbox.setString(s);
291: }
292:
293: private HSSFFont matchFont(Font font) {
294: HSSFColor hssfColor = workbook.getCustomPalette().findColor(
295: (byte) foreground.getRed(),
296: (byte) foreground.getGreen(),
297: (byte) foreground.getBlue());
298: if (hssfColor == null)
299: hssfColor = workbook.getCustomPalette().findSimilarColor(
300: (byte) foreground.getRed(),
301: (byte) foreground.getGreen(),
302: (byte) foreground.getBlue());
303: boolean bold = (font.getStyle() & Font.BOLD) != 0;
304: boolean italic = (font.getStyle() & Font.ITALIC) != 0;
305: HSSFFont hssfFont = workbook.findFont(
306: bold ? HSSFFont.BOLDWEIGHT_BOLD : 0, hssfColor
307: .getIndex(), (short) (font.getSize() * 20),
308: font.getName(), italic, false, (short) 0, (byte) 0);
309: if (hssfFont == null) {
310: hssfFont = workbook.createFont();
311: hssfFont.setBoldweight(bold ? HSSFFont.BOLDWEIGHT_BOLD : 0);
312: hssfFont.setColor(hssfColor.getIndex());
313: hssfFont.setFontHeight((short) (font.getSize() * 20));
314: hssfFont.setFontName(font.getName());
315: hssfFont.setItalic(italic);
316: hssfFont.setStrikeout(false);
317: hssfFont.setTypeOffset((short) 0);
318: hssfFont.setUnderline((byte) 0);
319: }
320:
321: return hssfFont;
322: }
323:
324: public void drawString(AttributedCharacterIterator iterator, int x,
325: int y) {
326: if (logger.check(POILogger.WARN))
327: logger.log(POILogger.WARN, "drawString not supported");
328: }
329:
330: public void fillArc(int x, int y, int width, int height,
331: int startAngle, int arcAngle) {
332: if (logger.check(POILogger.WARN))
333: logger.log(POILogger.WARN, "fillArc not supported");
334: }
335:
336: public void fillOval(int x, int y, int width, int height) {
337: HSSFSimpleShape shape = escherGroup
338: .createShape(new HSSFChildAnchor(x, y, x + width, y
339: + height));
340: shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
341: shape.setLineStyle(HSSFShape.LINESTYLE_NONE);
342: shape.setFillColor(foreground.getRed(), foreground.getGreen(),
343: foreground.getBlue());
344: shape.setLineStyleColor(foreground.getRed(), foreground
345: .getGreen(), foreground.getBlue());
346: }
347:
348: /**
349: * Fills a (closed) polygon, as defined by a pair of arrays, which
350: * hold the <i>x</i> and <i>y</i> coordinates.
351: * <p>
352: * This draws the polygon, with <code>nPoint</code> line segments.
353: * The first <code>nPoint - 1</code> line segments are
354: * drawn between sequential points
355: * (<code>xPoints[i],yPoints[i],xPoints[i+1],yPoints[i+1]</code>).
356: * The final line segment is a closing one, from the last point to
357: * the first (assuming they are different).
358: * <p>
359: * The area inside of the polygon is defined by using an
360: * even-odd fill rule (also known as the alternating rule), and
361: * the area inside of it is filled.
362: * @param xPoints array of the <code>x</code> coordinates.
363: * @param yPoints array of the <code>y</code> coordinates.
364: * @param nPoints the total number of points in the polygon.
365: * @see java.awt.Graphics#drawPolygon(int[], int[], int)
366: */
367: public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {
368: int right = findBiggest(xPoints);
369: int bottom = findBiggest(yPoints);
370: int left = findSmallest(xPoints);
371: int top = findSmallest(yPoints);
372: HSSFPolygon shape = escherGroup
373: .createPolygon(new HSSFChildAnchor(left, top, right,
374: bottom));
375: shape.setPolygonDrawArea(right - left, bottom - top);
376: shape.setPoints(addToAll(xPoints, -left), addToAll(yPoints,
377: -top));
378: shape.setLineStyleColor(foreground.getRed(), foreground
379: .getGreen(), foreground.getBlue());
380: shape.setFillColor(foreground.getRed(), foreground.getGreen(),
381: foreground.getBlue());
382: }
383:
384: private int findBiggest(int[] values) {
385: int result = Integer.MIN_VALUE;
386: for (int i = 0; i < values.length; i++) {
387: if (values[i] > result)
388: result = values[i];
389: }
390: return result;
391: }
392:
393: private int findSmallest(int[] values) {
394: int result = Integer.MAX_VALUE;
395: for (int i = 0; i < values.length; i++) {
396: if (values[i] < result)
397: result = values[i];
398: }
399: return result;
400: }
401:
402: public void fillRect(int x, int y, int width, int height) {
403: HSSFSimpleShape shape = escherGroup
404: .createShape(new HSSFChildAnchor(x, y, x + width, y
405: + height));
406: shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE);
407: shape.setLineStyle(HSSFShape.LINESTYLE_NONE);
408: shape.setFillColor(foreground.getRed(), foreground.getGreen(),
409: foreground.getBlue());
410: shape.setLineStyleColor(foreground.getRed(), foreground
411: .getGreen(), foreground.getBlue());
412: }
413:
414: public void fillRoundRect(int x, int y, int width, int height,
415: int arcWidth, int arcHeight) {
416: if (logger.check(POILogger.WARN))
417: logger.log(POILogger.WARN, "fillRoundRect not supported");
418: }
419:
420: public Shape getClip() {
421: return getClipBounds();
422: }
423:
424: public Rectangle getClipBounds() {
425: return null;
426: }
427:
428: public Rectangle getClipRect() {
429: return getClipBounds();
430: }
431:
432: public Color getColor() {
433: return foreground;
434: }
435:
436: public Font getFont() {
437: return font;
438: }
439:
440: public FontMetrics getFontMetrics(Font f) {
441: return Toolkit.getDefaultToolkit().getFontMetrics(f);
442: }
443:
444: public void setClip(int x, int y, int width, int height) {
445: setClip(((Shape) (new Rectangle(x, y, width, height))));
446: }
447:
448: public void setClip(Shape shape) {
449: // ignore... not implemented
450: }
451:
452: public void setColor(Color color) {
453: foreground = color;
454: }
455:
456: public void setFont(Font f) {
457: font = f;
458: }
459:
460: public void setPaintMode() {
461: if (logger.check(POILogger.WARN))
462: logger.log(POILogger.WARN, "setPaintMode not supported");
463: }
464:
465: public void setXORMode(Color color) {
466: if (logger.check(POILogger.WARN))
467: logger.log(POILogger.WARN, "setXORMode not supported");
468: }
469:
470: public void translate(int x, int y) {
471: if (logger.check(POILogger.WARN))
472: logger.log(POILogger.WARN, "translate not supported");
473: }
474:
475: public Color getBackground() {
476: return background;
477: }
478:
479: public void setBackground(Color background) {
480: this .background = background;
481: }
482:
483: HSSFShapeGroup getEscherGraphics() {
484: return escherGroup;
485: }
486: }
|