import java.awt.geom.*; import java.io.*; import java.awt.font.*; import java.awt.*; import java.util.*; import java.awt.image.*; import java.awt.image.renderable.*; import java.text.*; import javax.swing.*; public class GraphicsV extends Graphics2D { File out; Graphics2D superior; JComponent comp; int format; String contents; //file contents //for WMF files ByteArrayOutputStream storCont, storHeader; int nObj, maxSize, brushNum; public static final int SVG = 1; public static final int WMF = 2; Color c; Font f; //WMF Definitions public static final int MAPMODE = 0x0103; public static final int WINORG = 0x020B; public static final int WINEXT = 0x020C; public static final int BKGMODE = 0x0102; public static final int BKGCOLOR = 0x0201; public static final int EOF = 0x0000; public static final int SELECT = 0x012D; public static final int BRUSH = 0x02FC; public static final int PEN = 0x02FA; public static final int MOVETO = 0x0214; public static final int LINETO = 0x0213; public static final int ELLIPSE = 0x0418; public static final int RECTANGLE = 0x041B; public static final int POLYLINE = 0x0325; public static final int POLYGON = 0x0324; public static final int PIE = 0x081A; public static final int FONT = 0x02FB; public static final int TXTCOL = 0x0209; public static final int TXTALI = 0x012E; public static final int TEXT = 0x0A32; public GraphicsV(File out, JComponent comp, int format) { this.out = out; this.superior = (Graphics2D)comp.getGraphics(); this.comp = comp; this.format = format; c = Color.black; f = new Font("Dialog",Font.PLAIN,12); contents = ""; storCont = new ByteArrayOutputStream(); storHeader = new ByteArrayOutputStream(); nObj = 0; maxSize = 0; brushNum = -1; //header if(format == SVG) { contents += "\n"; contents += "\n"; contents += "\n"; } else { int[] mapmode = { 8 }; writeRecord(MAPMODE, mapmode, true, null); //set map mode int[] origin = { 0 , 0 }; int[] extent = { (int)comp.getWidth(), (int)comp.getHeight() }; writeRecord(WINORG, origin, true, null); //set window origin writeRecord(WINEXT, extent, true, null); //set window extent int[] one = { 2 }; writeRecord(BKGMODE, one, true, null); //bkg mode-opaque setColor(c); } } public void output() { //finish file if(format == SVG) { contents += "\n"; } else { try { int[] zero = new int[0]; writeRecord(EOF, zero, true, null); //do header here //placeable metafile header writeInt(storHeader, 0x9AC6CDD7); //key writeShort(storHeader, 0); //reserved writeShort(storHeader, 0); //x0 writeShort(storHeader, 0); //y0 writeShort(storHeader, comp.getWidth()); writeShort(storHeader, comp.getHeight()); writeShort(storHeader, 96); //resolution in twips/in //1440 writeInt(storHeader, 0); //reserved writeShort(storHeader, checkSum(storHeader.toByteArray())); //(storHeader, 0x9AC6 ^ 0xCDD7 ^ 0 ^ 0 ^ 0 ^ comp.getWidth() ^ comp.getHeight() ^ 1440 ^ 0 ^ 0); //checksum //standard metafile header writeShort(storHeader, 1); //disk writeShort(storHeader, 9); //header size writeShort(storHeader, 0x0300); //MS-WIN version writeInt(storHeader, storCont.size()/2+9+11); //size of file in words if(brushNum < 0) brushNum = 0; writeShort(storHeader, brushNum); //num objects writeInt(storHeader, maxSize); //max record size writeShort(storHeader, 0); //reserved } catch(IOException ioe) { //meaningless } } //write to file out.delete(); try { RandomAccessFile raf = new RandomAccessFile(out, "rw"); if(format == SVG) { raf.writeBytes(contents); } else { raf.write(storHeader.toByteArray()); raf.write(storCont.toByteArray()); } raf.close(); } catch(IOException ioe) { System.out.println(ioe.toString()); } contents = null; storCont = null; storHeader = null; } public String getHTMLColor(Color c) { //the idea is: #RRGGBB; added the first term to fill in zeros double toConv = 1*Math.pow(16,6)+c.getRed()*Math.pow(16,4)+c.getGreen()*Math.pow(16,2)+c.getBlue()*Math.pow(16,0); String hex = Integer.toHexString((int)toConv).toUpperCase(); return ("#"+hex.substring(1)); } public byte[] getBinaryColor(Color c) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(4); DataOutputStream dos = new DataOutputStream(baos); dos.writeByte(c.getRed()); dos.writeByte(c.getGreen()); dos.writeByte(c.getBlue()); dos.writeByte(0); return baos.toByteArray(); } catch(IOException ioe) { return null; } } public byte[] getFontName(Font f) { if(f.getFamily().equals("dialog")) { byte[] name = { (byte)65, (byte)114, (byte)105, (byte)97, (byte)108, (byte)0, (byte)0, (byte)48 }; //(Arial) return name; } else if(f.getFamily().equals("serif")) { byte[] name = { (byte)84, (byte)105, (byte)109, (byte)101, (byte)115, (byte)32, (byte)78, (byte)101, (byte)119, (byte)32, (byte)82, (byte)111, (byte)109, (byte)97, (byte)110, (byte)0, (byte)0, (byte)147 }; //(Times New Roman) return name; } else { System.out.println("Error: Font Family Not Recognized: "+f.getFamily()); byte[] name = { (byte)65, (byte)114, (byte)105, (byte)97, (byte)108, (byte)0, (byte)0, (byte)48 }; //(Arial) return name; } } public int checkSum(byte[] b) { short xor = 0; ByteArrayInputStream bais = new ByteArrayInputStream(b); DataInputStream dis = new DataInputStream(bais); while(true) { try { xor ^= dis.readShort(); } catch (EOFException eof) { break; } catch (IOException ioe) { return 0; } } bais = null; dis = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeShort(xor); byte[] litEnd = baos.toByteArray(); byte[] bigEnd = { litEnd[1], litEnd[0] }; bais = new ByteArrayInputStream(bigEnd); dis = new DataInputStream(bais); int newxor = (int)dis.readShort(); bais = null; dis = null; return newxor; } catch(IOException ioe) { return 0; } } public String getFontStyle(int fs) { if(fs == Font.BOLD) return "font-weight=\"bold\""; else if(fs == Font.ITALIC) return "font-style=\"italic\""; else return ""; } public void writeRecord(int func, int[] params, boolean reverse, byte[] direct) { //version for integer array try { int recSize = 2+1+params.length; if(direct != null) recSize += direct.length/2 - 1; //(we assume direct byte writing is only occuring ONCE in a record) if(recSize > maxSize) maxSize = recSize; writeInt(storCont, recSize); writeShort(storCont, func); if(reverse) { for(int i = params.length-1; i >= 0; i--) { //store params in reverse if(params[i] != -1) writeShort(storCont, params[i]); else storCont.write(direct); } } else { for(int i = 0; i < params.length; i++) { //store params in reverse if(params[i] != -1) writeShort(storCont, params[i]); else storCont.write(direct); } } nObj++; } catch(IOException ioe) { System.out.println("Error in WMF generation!"); } } public void writeShort(ByteArrayOutputStream out, int i) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeShort(i); byte[] bigEnd = baos.toByteArray(); if(bigEnd.length != 2) { System.out.println("Weird behavior from ByteArrayOutputStream!"); return; } byte[] litEnd = { bigEnd[1], bigEnd[0] }; out.write(litEnd); baos = null; dos = null; } public void writeInt(ByteArrayOutputStream out, int i) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeInt(i); byte[] bigEnd = baos.toByteArray(); if(bigEnd.length != 4) { System.out.println("Weird behavior from ByteArrayOutputStream!"); return; } byte[] litEnd = { bigEnd[3], bigEnd[2], bigEnd[1], bigEnd[0] }; out.write(litEnd); baos = null; dos = null; } public byte[] getBytes(String s) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeBytes(s); if(dos.size() % 2 != 0) dos.writeByte(0); byte[] bytes = baos.toByteArray(); baos = null; dos = null; return bytes; } catch(IOException ioe) { return null; } } public void writeBrushAndPen(boolean filled) { //create "brush" int[] brushtype = new int[3]; brushtype[0] = 0; brushtype[1] = -1; if(filled) brushtype[2] = 0; //1=not filled, 0=filled else brushtype[2] = 1; writeRecord(BRUSH, brushtype, true, getBinaryColor(c)); brushNum++; int[] select = { brushNum }; writeRecord(SELECT, select, true, null); //create "pen" int[] pentype = new int[4]; pentype[0] = -1; pentype[1] = 0; pentype[2] = 0; pentype[3] = 0; writeRecord(PEN, pentype, true, getBinaryColor(c)); brushNum++; int[] select2 = { brushNum }; writeRecord(SELECT, select2, true, null); } //////////////***********Begin Drawing Commands*****************\\\\\\\\\\\\\\\\\\\ public void draw(Shape s) { if(s instanceof Line2D) { Line2D line = (Line2D)s; if(format == SVG) { contents += "\n"; return; } else { writeBrushAndPen(false); int[] point1 = { (int)line.getX1(), (int)line.getY1() }; writeRecord(MOVETO,point1, true, null); int[] point2 = { (int)line.getX2(), (int)line.getY2() }; writeRecord(LINETO,point2, true, null); return; } } if(s instanceof Ellipse2D) { Ellipse2D ellipse = (Ellipse2D)s; if(format == SVG) { double radx = ellipse.getMaxX()-ellipse.getCenterX(); double rady = ellipse.getMaxY()-ellipse.getCenterY(); contents += "\n"; return; } else { writeBrushAndPen(false); int[] points = { (int)ellipse.getMinX(), (int)ellipse.getMinY(), (int)ellipse.getMaxX(), (int)ellipse.getMaxY() }; writeRecord(ELLIPSE,points, true, null); return; } } if(s instanceof Rectangle2D) { Rectangle2D rect = (Rectangle2D)s; if(format == SVG) { contents += "\n"; return; } else { writeBrushAndPen(false); int[] points = { (int)rect.getMinX(), (int)rect.getMinY(), (int)rect.getMaxX(), (int)rect.getMaxY() }; writeRecord(RECTANGLE,points, true, null); return; } } if(s instanceof GeneralPath) { GeneralPath path = (GeneralPath)s; PathIterator it = path.getPathIterator(superior.getTransform()); if(format == SVG) { contents += "\n"; return; } else { Vector v = new Vector(); while(!it.isDone()) { double[] points = new double[6]; int type = it.currentSegment(points); if(type == PathIterator.SEG_LINETO || type == PathIterator.SEG_MOVETO) { int[] point = { (int)points[0], (int)points[1] }; v.add(point); } else if(type == PathIterator.SEG_CLOSE) { } else { System.out.println("Path Segment type not supported: "+type); } it.next(); } int[] poly = new int[v.size()*2+1]; poly[0] = v.size(); int p = 1; for(int i = 1; i < v.size(); i++) { int[] point = (int[])v.elementAt(i); poly[p] = point[0]; poly[p+1] = point[1]; p += 2; } writeBrushAndPen(false); writeRecord(POLYLINE,poly,false, null); return; } } if(s instanceof Arc2D) { Arc2D arc = (Arc2D)s; if(format == SVG) { //implement as a path PathIterator it = arc.getPathIterator(superior.getTransform()); contents += "\n"; return; } else { writeBrushAndPen(false); Rectangle2D bounds = arc.getBounds(); Point2D end = arc.getStartPoint(); Point2D start = arc.getEndPoint(); int[] points = { (int)bounds.getMinX(), (int)bounds.getMinY(), (int)bounds.getMaxX(), (int)bounds.getMaxY(), (int)start.getX(), (int)start.getY(), (int)end.getX(), (int)end.getY() }; writeRecord(PIE,points, true, null); return; } } System.out.println("Shape not supported: "+s.getClass()); } public void fill(Shape s) { if(s instanceof Ellipse2D) { Ellipse2D ellipse = (Ellipse2D)s; if(format == SVG) { double radx = ellipse.getMaxX()-ellipse.getCenterX(); double rady = ellipse.getMaxY()-ellipse.getCenterY(); contents += "\n"; return; } else { writeBrushAndPen(true); int[] points = { (int)ellipse.getMinX(), (int)ellipse.getMinY(), (int)ellipse.getMaxX(), (int)ellipse.getMaxY() }; writeRecord(ELLIPSE,points, true, null); return; } } if(s instanceof Rectangle2D) { Rectangle2D rect = (Rectangle2D)s; if(format == SVG) { contents += "\n"; return; } else { writeBrushAndPen(true); int[] points = { (int)rect.getMinX(), (int)rect.getMinY(), (int)rect.getMaxX(), (int)rect.getMaxY() }; writeRecord(RECTANGLE,points, true, null); return; } } if(s instanceof GeneralPath) { GeneralPath path = (GeneralPath)s; PathIterator it = path.getPathIterator(superior.getTransform()); if(format == SVG) { contents += "\n"; return; } else { Vector v = new Vector(); while(!it.isDone()) { double[] points = new double[6]; int type = it.currentSegment(points); if(type == PathIterator.SEG_LINETO || type == PathIterator.SEG_MOVETO) { int[] point = { (int)points[0], (int)points[1] }; v.addElement(point); } else if(type == PathIterator.SEG_CLOSE) { } else { System.out.println("Path Segment type not supported: "+type); } it.next(); } int[] poly = new int[v.size()*2+1]; poly[0] = v.size(); int p = 1; for(int i = 0; i < v.size(); i++) { int[] point = (int[])v.elementAt(i); poly[p] = point[0]; poly[p+1] = point[1]; p += 2; } writeBrushAndPen(true); writeRecord(POLYGON,poly,false, null); return; } } if(s instanceof Arc2D) { Arc2D arc = (Arc2D)s; if(format == SVG) { //implement as a path PathIterator it = arc.getPathIterator(superior.getTransform()); contents += "\n"; return; } else { writeBrushAndPen(true); Rectangle2D bounds = arc.getBounds(); Point2D end = arc.getStartPoint(); Point2D start = arc.getEndPoint(); int[] points = { (int)bounds.getMinX(), (int)bounds.getMinY(), (int)bounds.getMaxX(), (int)bounds.getMaxY(), (int)start.getX(), (int)start.getY(), (int)end.getX(), (int)end.getY() }; writeRecord(PIE,points, true, null); return; } } System.out.println("Shape not supported: "+s.getClass()); } public void drawString(String s, float x, float y) { if(format == SVG) { contents += ""; contents += s+"\n"; } else { //set font int weight; if(f.getStyle() == Font.BOLD) weight = 700; else weight = 400; int[] fontprops = { f.getSize(), 0, 0, 0, weight, -1 }; byte[] name = getFontName(f); byte[] fontprops2 = new byte[8+name.length]; if(f.getStyle() == Font.ITALIC) //italic fontprops2[0] = 1; else fontprops2[0] = 0; fontprops2[1] = 0; //underline fontprops2[2] = 0; //strikeout fontprops2[3] = 0; //charset fontprops2[4] = 0; //outprecision fontprops2[5] = 0; //clip precision fontprops2[6] = 0; //quality fontprops2[7] = 34; //pitch&family for(int i = 0; i < name.length; i++) { fontprops2[i+8] = name[i]; } writeRecord(FONT,fontprops,false,fontprops2); brushNum++; int[] select = { brushNum }; writeRecord(SELECT, select, false, null); //set color int[] col = { -1 }; writeRecord(TXTCOL,col,true,getBinaryColor(c)); //set alignment int[] align = { 24 }; writeRecord(TXTALI,align,true,null); //write string byte[] strInBytes = getBytes(s); int[] params = { (int)y, (int)x, strInBytes.length, 0, -1 }; writeRecord(TEXT, params, false, strInBytes); } } public void drawString(String s, int x, int y) { drawString(s,(float)x,(float)y); } public FontMetrics getFontMetrics() { return superior.getFontMetrics(f); } public void setColor(Color c) { this.c = c; } public Color getColor() { return c; } public void setFont(Font f) { this.f = f; } public Font getFont() { return f; } //***********************End Implemented Drawing Commands**************************// public void setStroke(Stroke s) { superior.setStroke(s); } public void setPaint(Paint p) { System.out.println(p); } public void drawString(AttributedCharacterIterator iterator, float x, float y) { superior.drawString(iterator, x, y); } public void drawString(AttributedCharacterIterator iterator, int x, int y) { superior.drawString(iterator, x, y); } public FontRenderContext getFontRenderContext() { return superior.getFontRenderContext(); } public void clip(Shape s) { superior.clip(s); } public Stroke getStroke() { return superior.getStroke(); } public Color getBackground() { return superior.getBackground(); } public void setBackground(Color c) { superior.setBackground(c); } public Composite getComposite() { return superior.getComposite(); } public Paint getPaint() { return superior.getPaint(); } public AffineTransform getTransform() { return superior.getTransform(); } public void setTransform(AffineTransform at) { superior.setTransform(at); } public void transform(AffineTransform at) { superior.transform(at); } public void shear(double shx, double shy) { superior.shear(shx, shy); } public void scale(double sx, double sy) { superior.scale(sx, sy); } public void rotate(double theta, double x, double y) { superior.rotate(theta, x, y); } public void rotate(double theta) { superior.rotate(theta); } public void translate(double tx, double ty) { superior.translate(tx, ty); } public void translate(int tx, int ty) { superior.translate(tx, ty); } public RenderingHints getRenderingHints() { return superior.getRenderingHints(); } public void addRenderingHints(Map m) { superior.addRenderingHints(m); } public void setRenderingHints(Map m) { superior.setRenderingHints(m); } public Object getRenderingHint(RenderingHints.Key hintKey) { return superior.getRenderingHint(hintKey); } public void setRenderingHint(RenderingHints.Key hintKey, Object o) { superior.setRenderingHint(hintKey, o); } public void setComposite(Composite c) { superior.setComposite(c); } public GraphicsConfiguration getDeviceConfiguration() { return superior.getDeviceConfiguration(); } public boolean hit(Rectangle rect, Shape s, boolean onStroke) { return superior.hit(rect, s, onStroke); } public void drawGlyphVector(GlyphVector g, float x, float y) { superior.drawGlyphVector(g, x, y); } public void drawRenderableImage(RenderableImage image, AffineTransform at) { superior.drawRenderableImage(image, at); } public void drawRenderedImage(RenderedImage image, AffineTransform at) { superior.drawRenderedImage(image, at); } public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { superior.drawImage(img, op, x, y); } public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { return superior.drawImage(img, xform, obs); } public void dispose() { } public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { return superior.drawImage(img, x, y, width, height, bgcolor, observer); } 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) { return superior.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); } public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { return superior.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); } public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { return superior.drawImage(img, x, y, bgcolor, observer); } public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { return superior.drawImage(img, x, y, width, height, observer); } public boolean drawImage(Image img, int x, int y, ImageObserver observer) { return superior.drawImage(img, x, y, observer); } public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { superior.fillPolygon(xPoints, yPoints, nPoints); } public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { superior.drawPolygon(xPoints, yPoints, nPoints); } public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { superior.drawPolyline(xPoints, yPoints, nPoints); } public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { superior.fillArc(x, y, width, height, startAngle, arcAngle); } public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { superior.drawArc(x, y, width, height, startAngle, arcAngle); } public void fillOval(int x, int y, int width, int height) { superior.fillOval(x, y, width, height); } public void drawOval(int x, int y, int width, int height) { superior.drawOval(x, y, width, height); } public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { superior.fillRoundRect(x, y, width, height, arcWidth, arcHeight); } public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { superior.drawRoundRect(x, y, width, height, arcWidth, arcHeight); } public void clearRect(int x, int y, int width, int height) { superior.clearRect(x, y, width, height); } public void fillRect(int x, int y, int width, int height) { superior.fillRect(x, y, width, height); } public void drawLine(int x1, int y1, int x2, int y2) { superior.drawLine(x1, y1, x2, y2); } public void copyArea(int x, int y, int width, int height, int dx, int dy) { superior.copyArea(x, y, width, height, dx, dy); } public void setClip(int x, int y, int width, int height) { superior.setClip(x, y, width, height); } public void setClip(Shape s) { superior.setClip(s); } public Shape getClip() { return superior.getClip(); } public void clipRect(int x, int y, int width, int height) { superior.clipRect(x, y, width, height); } public Rectangle getClipBounds() { return superior.getClipBounds(); } public FontMetrics getFontMetrics(Font f) { return superior.getFontMetrics(f); } public void setXORMode(Color c1) { superior.setXORMode(c1); } public void setPaintMode() { superior.setPaintMode(); } public Graphics create() { return superior; } }