2
* This program is free software; you can redistribute it and/or modify
3
* it under the terms of the GNU General Public License as published by
4
* the Free Software Foundation; either version 2 of the License, or
5
* (at your option) any later version.
7
* This program is distributed in the hope that it will be useful,
8
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
* GNU General Public License for more details.
12
* You should have received a copy of the GNU General Public License
13
* along with this program; if not, write to the Free Software
14
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
* PostscriptGraphics.java
19
* Copyright (C) 2003 University of Waikato, Hamilton, New Zealand
23
package weka.gui.visualize;
25
import java.awt.image.renderable.*;
27
import java.awt.geom.*;
28
import java.awt.font.*;
31
import java.awt.image.*;
36
* The PostscriptGraphics class extends the Graphics2D class to
37
* produce an encapsulated postscript file rather than on-screen display.
39
* Currently only a small (but useful) subset of Graphics methods have been
41
* To handle the ability to Clone a Graphics object, the graphics state of the
42
* eps is set from the graphics state of the local PostscriptGraphics before output.
43
* To use, create a PostscriptGraphics object, and pass it to the PaintComponent
44
* method of a JComponent.
46
* If necessary additional font replacements can be inserted, since some fonts
47
* might be displayed incorrectly.
49
* @see #addPSFontReplacement(String, String)
50
* @see #m_PSFontReplacement
51
* @author Dale Fletcher (dale@cs.waikato.ac.nz)
52
* @author FracPete (fracpete at waikato dot ac dot nz)
53
* @version $Revision: 1.4 $
56
public class PostscriptGraphics extends Graphics2D {
59
* This inner class is used to maintain the graphics states of the PostScript
60
* file and graphics context.
62
private class GraphicsState {
63
/** The current pen color */
64
protected Color m_currentColor;
66
/** The current Font */
67
protected Font m_currentFont;
69
/** The current Stroke (not yet used) */
70
protected Stroke m_currentStroke;
72
/** x,y Translation */
73
protected int m_xOffset;
74
protected int m_yOffset;
76
/** the scale factors */
77
protected double m_xScale;
78
protected double m_yScale;
81
* Create a new GraphicsState with default values.
84
m_currentColor = Color.white;
85
m_currentFont = new Font ("Courier", Font.PLAIN, 11);
86
m_currentStroke = new BasicStroke();
94
* Create a new cloned GraphicsState
96
* @param copy The GraphicsState to clone
98
GraphicsState(GraphicsState copy){
99
m_currentColor = copy.m_currentColor;
100
m_currentFont = copy.m_currentFont;
101
m_currentStroke = copy.m_currentStroke;
102
m_xOffset = copy.m_xOffset;
103
m_yOffset = copy.m_yOffset;
104
m_xScale = copy.m_xScale;
105
m_yScale = copy.m_yScale;
109
protected Stroke getStroke(){
110
return m_currentStroke;
113
protected void setStroke(Stroke s){
118
protected Font getFont(){
119
return m_currentFont;
122
protected void setFont(Font f){
127
protected Color getColor(){
128
return m_currentColor;
131
protected void setColor(Color c){
135
/* Translation methods */
136
protected void setXOffset(int xo){
140
protected void setYOffset(int yo){
144
protected int getXOffset(){
148
protected int getYOffset(){
152
protected void setXScale(double x){
156
protected void setYScale(double y){
160
protected double getXScale(){
164
protected double getYScale(){
169
/** The bounding box of the output */
170
protected Rectangle m_extent;
172
/** The output file */
173
protected PrintStream m_printstream;
175
/** The current global PostScript graphics state for all cloned objects */
176
protected GraphicsState m_psGraphicsState;
178
/** The current local graphics state for this PostscriptGraphics object */
179
protected GraphicsState m_localGraphicsState;
181
/** whether to print some debug information */
182
protected final static boolean DEBUG = false;
184
/** the font replacement */
185
protected static Hashtable m_PSFontReplacement;
187
/** output if we're in debug mode */
190
System.err.println(PostscriptGraphics.class.getName() + ": DEBUG ON");
192
// get font replacements
193
m_PSFontReplacement = new Hashtable();
194
m_PSFontReplacement.put("SansSerif.plain", "Helvetica.plain"); // SansSerif.plain is displayed as Courier in GV???
195
m_PSFontReplacement.put("Dialog.plain", "Helvetica.plain"); // dialog is a Sans Serif font, but GV displays it as Courier???
196
m_PSFontReplacement.put("Microsoft Sans Serif", "Helvetica.plain"); // MS Sans Serif is a Sans Serif font (hence the name!), but GV displays it as Courier???
197
m_PSFontReplacement.put("MicrosoftSansSerif", "Helvetica.plain"); // MS Sans Serif is a Sans Serif font (hence the name!), but GV displays it as Courier???
202
* Creates a new PostscriptGraphics object, given dimensions and
205
* @param width The width of eps in points.
206
* @param height The height of eps in points.
207
* @param os File to send postscript to.
209
public PostscriptGraphics(int width, int height, OutputStream os ){
211
m_extent = new Rectangle(0, 0, height, width);
212
m_printstream = new PrintStream(os);
213
m_localGraphicsState = new GraphicsState();
214
m_psGraphicsState = new GraphicsState();
220
* Creates a new cloned PostscriptGraphics object.
222
* @param copy The PostscriptGraphics object to clone.
224
PostscriptGraphics(PostscriptGraphics copy){
226
m_extent = new Rectangle(copy.m_extent);
227
m_printstream = copy.m_printstream;
228
m_localGraphicsState = new GraphicsState(copy.m_localGraphicsState); // create a local copy of the current state
229
m_psGraphicsState = copy.m_psGraphicsState; // link to global state of eps file
233
* Finalizes output file.
235
public void finished(){
236
m_printstream.flush();
240
* Output postscript header to PrintStream, including helper macros.
242
private void Header(){
243
m_printstream.println("%!PS-Adobe-3.0 EPSF-3.0");
244
m_printstream.println("%%BoundingBox: 0 0 " + xScale(m_extent.width) + " " + yScale(m_extent.height));
245
m_printstream.println("%%CreationDate: " + Calendar.getInstance().getTime());
247
m_printstream.println("/Oval { % x y w h filled");
248
m_printstream.println("gsave");
249
m_printstream.println("/filled exch def /h exch def /w exch def /y exch def /x exch def");
250
m_printstream.println("x w 2 div add y h 2 div sub translate");
251
m_printstream.println("1 h w div scale");
252
m_printstream.println("filled {0 0 moveto} if");
253
m_printstream.println("0 0 w 2 div 0 360 arc");
254
m_printstream.println("filled {closepath fill} {stroke} ifelse grestore} bind def");
256
m_printstream.println("/Rect { % x y w h filled");
257
m_printstream.println("/filled exch def /h exch def /w exch def /y exch def /x exch def");
258
m_printstream.println("newpath ");
259
m_printstream.println("x y moveto");
260
m_printstream.println("w 0 rlineto");
261
m_printstream.println("0 h neg rlineto");
262
m_printstream.println("w neg 0 rlineto");
263
m_printstream.println("closepath");
264
m_printstream.println("filled {fill} {stroke} ifelse} bind def");
266
m_printstream.println("%%BeginProlog\n%%EndProlog");
267
m_printstream.println("%%Page 1 1");
268
setFont(null); // set to default
269
setColor(null); // set to default
270
setStroke(null); // set to default
274
* adds the PS font name to replace and its replacement in the replacement
277
* @param replace the PS font name to replace
278
* @param with the PS font name to replace the font with
280
public static void addPSFontReplacement(String replace, String with) {
281
m_PSFontReplacement.put(replace, with);
285
* Convert Java Y coordinate (0 = top) to PostScript (0 = bottom)
286
* Also apply current Translation
287
* @param y Java Y coordinate
288
* @return translated Y to postscript
290
private int yTransform(int y){
291
return (m_extent.height - (m_localGraphicsState.getYOffset() + y));
295
* Apply current X Translation
296
* @param x Java X coordinate
297
* @return translated X to postscript
299
private int xTransform(int x){
300
return (m_localGraphicsState.getXOffset() + x);
304
* scales the given number with the provided scale factor
306
private int doScale(int number, double factor) {
307
return (int) StrictMath.round(number * factor);
311
* scales the given x value with current x scale factor
313
private int xScale(int x) {
314
return doScale(x, m_localGraphicsState.getXScale());
318
* scales the given y value with current y scale factor
320
private int yScale(int y) {
321
return doScale(y, m_localGraphicsState.getYScale());
324
/** Set the current eps graphics state to that of the local one
326
private void setStateToLocal(){
327
setColor(this.getColor());
328
setFont(this.getFont());
329
setStroke(this.getStroke());
333
* returns a two hexadecimal representation of i, if shorter than 2 chars
334
* then an additional "0" is put in front
336
private String toHex(int i) {
339
result = Integer.toHexString(i);
340
if (result.length() < 2)
341
result = "0" + result;
346
/***** overridden Graphics methods *****/
349
* Draw a filled rectangle with the background color.
351
* @param x starting x coord
352
* @param y starting y coord
353
* @param width rectangle width
354
* @param height rectangle height
356
public void clearRect(int x, int y, int width, int height) {
358
Color saveColor = getColor();
359
setColor(Color.white); // background color for page
360
m_printstream.println(xTransform(xScale(x)) + " " + yTransform(yScale(y)) + " " + xScale(width) + " " + yScale(height) + " true Rect");
367
public void clipRect(int x, int y, int width, int height) {}
372
public void copyArea(int x, int y, int width, int height, int dx, int dy) {}
375
* Clone a PostscriptGraphics object
377
public Graphics create() {
379
m_printstream.println("%create");
380
PostscriptGraphics psg = new PostscriptGraphics(this);
387
public void dispose(){}
390
* Draw an outlined rectangle with 3D effect in current pen color.
391
* (Current implementation: draw simple outlined rectangle)
393
* @param x starting x coord
394
* @param y starting y coord
395
* @param width rectangle width
396
* @param height rectangle height
397
* @param raised True: appear raised, False: appear etched
399
public void draw3DRect(int x, int y, int width, int height, boolean raised){
400
drawRect(x,y,width,height);
406
public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle){}
409
* simply calls drawString(String,int,int)
411
* @see #drawString(String,int,int)
413
public void drawBytes(byte[] data, int offset, int length, int x, int y) {
414
drawString(new String(data, offset, length), x, y);
418
* simply calls drawString(String,int,int)
420
* @see #drawString(String,int,int)
422
public void drawChars(char[] data, int offset, int length, int x, int y) {
423
drawString(new String(data, offset, length), x, y);
427
* calls drawImage(Image,int,int,int,int,Color,ImageObserver)
429
* @see #drawImage(Image,int,int,int,int,Color,ImageObserver)
431
public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer){
432
return drawImage(img, x, y, img.getWidth(observer), img.getHeight(observer), bgcolor, observer);
436
* calls drawImage(Image,int,int,Color,ImageObserver) with Color.WHITE as
439
* @see #drawImage(Image,int,int,Color,ImageObserver)
442
public boolean drawImage(Image img, int x, int y, ImageObserver observer){
443
return drawImage(img, x, y, Color.WHITE, observer);
447
* PS see http://astronomy.swin.edu.au/~pbourke/geomformats/postscript/
448
* Java http://show.docjava.com:8086/book/cgij/doc/ip/graphics/SimpleImageFrame.java.html
450
public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer){
452
// get data from image
453
int[] pixels = new int[width * height];
454
PixelGrabber grabber = new PixelGrabber(img, 0, 0, width, height, pixels, 0, width);
455
grabber.grabPixels();
456
ColorModel model = ColorModel.getRGBdefault();
459
m_printstream.println("gsave");
460
m_printstream.println(xTransform(xScale(x)) + " " + (yTransform(yScale(y)) - yScale(height)) + " translate");
461
m_printstream.println(xScale(width) + " " + yScale(height) + " scale");
462
m_printstream.println(width + " " + height + " " + "8" + " [" + width + " 0 0 " + (-height) + " 0 " + height + "]");
463
m_printstream.println("{<");
466
for (int i = 0; i < height; i++) {
467
for (int j = 0; j < width; j++) {
468
index = i * width + j;
469
m_printstream.print(toHex(model.getRed(pixels[index])));
470
m_printstream.print(toHex(model.getGreen(pixels[index])));
471
m_printstream.print(toHex(model.getBlue(pixels[index])));
473
m_printstream.println();
476
m_printstream.println(">}");
477
m_printstream.println("false 3 colorimage");
478
m_printstream.println("grestore");
481
catch (Exception e) {
488
* calls drawImage(Image,int,int,int,int,Color,ImageObserver) with the color
489
* WHITE as background
491
* @see #drawImage(Image,int,int,int,int,Color,ImageObserver)
494
public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer){
495
return drawImage(img, x, y, width, height, Color.WHITE, observer);
501
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer){
506
* calls drawImage(Image,int,int,int,int,int,int,int,int,Color,ImageObserver)
507
* with Color.WHITE as background color
509
* @see #drawImage(Image,int,int,int,int,int,int,int,int,Color,ImageObserver)
511
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer){
512
return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, Color.WHITE, observer);
517
* Draw a line in current pen color.
519
* @param x1 starting x coord
520
* @param y1 starting y coord
521
* @param x2 ending x coord
522
* @param y2 ending y coord
524
public void drawLine(int x1, int y1, int x2, int y2){
526
m_printstream.println(xTransform(xScale(x1)) + " " + yTransform(yScale(y1)) + " moveto " + xTransform(xScale(x2)) + " " + yTransform(yScale(y2)) + " lineto stroke");
530
* Draw an Oval outline in current pen color.
532
* @param x x-axis center of oval
533
* @param y y-axis center of oval
534
* @param width oval width
535
* @param height oval height
537
public void drawOval(int x, int y, int width, int height){
539
m_printstream.println(xTransform(xScale(x)) + " " + yTransform(yScale(y)) + " " + xScale(width) + " " + yScale(height) + " false Oval");
545
public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints){}
550
public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints){}
553
* Draw an outlined rectangle in current pen color.
555
* @param x starting x coord
556
* @param y starting y coord
557
* @param width rectangle width
558
* @param height rectangle height
560
public void drawRect(int x, int y, int width, int height){
562
m_printstream.println(xTransform(xScale(x)) + " " + yTransform(yScale(y)) + " " + xScale(width) + " " + yScale(height) + " false Rect");
568
public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight){}
573
public void drawString(AttributedCharacterIterator iterator, int x, int y){}
576
* Draw text in current pen color.
578
* @param str Text to output
579
* @param x starting x coord
580
* @param y starting y coord
582
public void drawString(String str, int x, int y){
584
m_printstream.println(xTransform(xScale(x)) + " " + yTransform(yScale(y)) + " moveto" + " (" + str + ") show stroke");
588
* Draw a filled rectangle with 3D effect in current pen color.
589
* (Current implementation: draw simple filled rectangle)
591
* @param x starting x coord
592
* @param y starting y coord
593
* @param width rectangle width
594
* @param height rectangle height
595
* @param raised True: appear raised, False: appear etched
597
public void fill3DRect(int x, int y, int width, int height, boolean raised){
598
fillRect(x, y, width, height);
604
public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle){}
607
* Draw a filled Oval in current pen color.
609
* @param x x-axis center of oval
610
* @param y y-axis center of oval
611
* @param width oval width
612
* @param height oval height
614
public void fillOval(int x, int y, int width, int height){
616
m_printstream.println(xTransform(xScale(x)) + " " + yTransform(yScale(y)) + " " + xScale(width) + " " + yScale(height) + " true Oval");
622
public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints){}
627
public void fillPolygon(Polygon p){}
630
* Draw a filled rectangle in current pen color.
632
* @param x starting x coord
633
* @param y starting y coord
634
* @param width rectangle width
635
* @param height rectangle height
638
public void fillRect(int x, int y, int width, int height){
639
if (width == m_extent.width && height == m_extent.height) {
640
clearRect(x, y, width, height); // if we're painting the entire background, just make it white
643
m_printstream.println("% fillRect");
645
m_printstream.println(xTransform(xScale(x)) + " " + yTransform(yScale(y)) + " " + xScale(width) + " " + yScale(height) + " true Rect");
652
public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight){}
657
public void finalize(){}
662
public Shape getClip(){
667
* This returns the full current drawing area
668
* @return full drawing area
670
public Rectangle getClipBounds(){
671
return(new Rectangle(0, 0, m_extent.width, m_extent.height));
675
* This returns the full current drawing area
676
* @return full drawing area
678
public Rectangle getClipBounds(Rectangle r) {
679
r.setBounds(0, 0, m_extent.width, m_extent.height);
686
public Rectangle getClipRect() {return null;}
689
* Get current pen color.
691
* @return current pen color.
693
public Color getColor(){
694
return (m_localGraphicsState.getColor());
700
* @return current font.
702
public Font getFont(){
703
return (m_localGraphicsState.getFont());
710
* @return Font metrics.
712
public FontMetrics getFontMetrics(Font f){
713
return(Toolkit.getDefaultToolkit().getFontMetrics(f));
720
public void setClip(int x, int y, int width, int height) {}
725
public void setClip(Shape clip){}
728
* Set current pen color. Default to black if null.
730
* @param c new pen color.
732
public void setColor(Color c){
734
m_localGraphicsState.setColor(c);
735
if (m_psGraphicsState.getColor().equals(c)) {
738
m_psGraphicsState.setColor(c);
740
m_localGraphicsState.setColor(Color.black);
741
m_psGraphicsState.setColor(getColor());
743
m_printstream.print(getColor().getRed()/255.0);
744
m_printstream.print(" ");
745
m_printstream.print(getColor().getGreen()/255.0);
746
m_printstream.print(" ");
747
m_printstream.print(getColor().getBlue()/255.0);
748
m_printstream.println(" setrgbcolor");
752
* replaces the font (PS name) if necessary and returns the new name
754
private static String replacePSFont(String font) {
759
// do we have to replace it? -> same style, size
760
if (m_PSFontReplacement.containsKey(font)) {
761
result = m_PSFontReplacement.get(font).toString();
763
System.out.println("switched font from '" + font + "' to '" + result + "'");
770
* Set current font. Default to Plain Courier 11 if null.
772
* @param font new font.
774
public void setFont(Font font){
777
m_localGraphicsState.setFont(font);
778
if ( font.getName().equals(m_psGraphicsState.getFont().getName())
779
&& (m_psGraphicsState.getFont().getStyle() == font.getStyle())
780
&& (m_psGraphicsState.getFont().getSize() == yScale(font.getSize())))
782
m_psGraphicsState.setFont(new Font(font.getName(), font.getStyle(), yScale(getFont().getSize())));
785
m_localGraphicsState.setFont(new Font ("Courier", Font.PLAIN, 11));
786
m_psGraphicsState.setFont(getFont());
789
m_printstream.println("/(" + replacePSFont(getFont().getPSName()) + ")" + " findfont");
790
m_printstream.println(yScale(getFont().getSize()) + " scalefont setfont");
796
public void setPaintMode(){}
801
public void setXORMode(Color c1){}
804
* Translates the origin of the graphics context to the point (x, y) in the
805
* current coordinate system. Modifies this graphics context so that its new
806
* origin corresponds to the point (x, y) in this graphics context's original
807
* coordinate system. All coordinates used in subsequent rendering operations
808
* on this graphics context will be relative to this new origin.
810
* @param x the x coordinate.
811
* @param y the y coordinate.
813
public void translate(int x, int y){
815
System.out.println("translate with x = " + x + " and y = " + y);
816
m_localGraphicsState.setXOffset(m_localGraphicsState.getXOffset() + xScale(x));
817
m_localGraphicsState.setYOffset(m_localGraphicsState.getYOffset() + yScale(y));
818
m_psGraphicsState.setXOffset(m_psGraphicsState.getXOffset() + xScale(x));
819
m_psGraphicsState.setYOffset(m_psGraphicsState.getYOffset() + yScale(y));
821
/***** END overridden Graphics methods *****/
823
/***** START overridden Graphics2D methods *****/
825
public FontRenderContext getFontRenderContext(){
826
return (new FontRenderContext(null,true,true));
828
public void clip(Shape s){}
829
public Stroke getStroke(){
830
return(m_localGraphicsState.getStroke());
833
public Color getBackground(){
836
public void setBackground(Color c){}
837
public Composite getComposite(){
838
return(AlphaComposite.getInstance(AlphaComposite.SRC));
840
public Paint getPaint(){
841
return((Paint) (new Color(getColor().getRed(),getColor().getGreen(),getColor().getBlue())));
843
public AffineTransform getTransform(){
844
return(new AffineTransform());
846
public void setTransform(AffineTransform at) {}
847
public void transform(AffineTransform at) {}
848
public void shear(double d1, double d2){}
849
public void scale(double d1, double d2) {
850
m_localGraphicsState.setXScale(d1);
851
m_localGraphicsState.setYScale(d2);
853
System.err.println("d1 = " + d1 + ", d2 = " + d2);
855
public void rotate(double d1, double d2, double d3){}
856
public void rotate(double d1){}
857
public void translate(double d1, double d2) {}
858
public RenderingHints getRenderingHints(){
859
return(new RenderingHints(null));
861
public void addRenderingHints(Map m){}
862
public void setRenderingHints(Map m){}
863
public Object getRenderingHint(RenderingHints.Key key){
866
public void setRenderingHint(RenderingHints.Key key, Object o){}
867
public void setStroke(Stroke s){
869
m_localGraphicsState.setStroke(s);
870
if (s.equals(m_psGraphicsState.getStroke())) {
873
m_psGraphicsState.setStroke(s);
875
m_localGraphicsState.setStroke(new BasicStroke());
876
m_psGraphicsState.setStroke(getStroke());
878
// ouput postscript here to set stroke.
880
public void setPaint(Paint p){
882
public void setComposite(Composite c){}
883
public GraphicsConfiguration getDeviceConfiguration(){
884
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
885
GraphicsDevice gd = ge.getDefaultScreenDevice();
886
return(gd.getDefaultConfiguration());
888
public boolean hit(Rectangle r, Shape s, boolean onstroke){
891
public void fill(Shape s){}
892
public void drawGlyphVector(GlyphVector gv, float f1, float f2){}
893
public void drawString(AttributedCharacterIterator aci, float f1, float f2){}
894
public void drawString(String str, float x, float y){
895
drawString(str,(int)x, (int)y);
897
public void drawRenderableImage(RenderableImage ri, AffineTransform at){}
898
public void drawRenderedImage(RenderedImage ri, AffineTransform af){}
899
public void drawImage(BufferedImage bi, BufferedImageOp bio, int i1, int i2){}
900
public boolean drawImage(Image im, AffineTransform at, ImageObserver io){
903
public void draw(Shape s){}