001package com.hfg.graphics;
002
003import java.awt.BasicStroke;
004import java.awt.Color;
005import java.awt.Composite;
006import java.awt.Font;
007import java.awt.FontMetrics;
008import java.awt.Graphics;
009import java.awt.Graphics2D;
010import java.awt.GraphicsConfiguration;
011import java.awt.Image;
012import java.awt.Paint;
013import java.awt.Point;
014import java.awt.Rectangle;
015import java.awt.RenderingHints;
016import java.awt.Shape;
017import java.awt.Stroke;
018import java.awt.font.GlyphVector;
019import java.awt.font.FontRenderContext;
020import java.awt.geom.Point2D;
021import java.awt.image.ImageObserver;
022import java.awt.image.BufferedImage;
023import java.awt.image.BufferedImageOp;
024import java.awt.image.RenderedImage;
025import java.awt.image.renderable.RenderableImage;
026import java.awt.geom.AffineTransform;
027import java.awt.geom.Line2D;
028import java.text.AttributedCharacterIterator;
029import java.util.Map;
030import java.util.Stack;
031
032import com.hfg.css.CSS;
033import com.hfg.exception.UnimplementedMethodException;
034import com.hfg.xml.XMLTag;
035import com.hfg.svg.*;
036
037//------------------------------------------------------------------------------
038/**
039 * Simple Graphics2D class which can be used to generate SVG. Not yet with full functionality.
040 * <div>
041 *   @author J. Alex Taylor, hairyfatguy.com
042 * </div>
043 */
044//------------------------------------------------------------------------------
045// com.hfg XML/HTML Coding Library
046//
047// This library is free software; you can redistribute it and/or
048// modify it under the terms of the GNU Lesser General Public
049// License as published by the Free Software Foundation; either
050// version 2.1 of the License, or (at your option) any later version.
051//
052// This library is distributed in the hope that it will be useful,
053// but WITHOUT ANY WARRANTY; without even the implied warranty of
054// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
055// Lesser General Public License for more details.
056//
057// You should have received a copy of the GNU Lesser General Public
058// License along with this library; if not, write to the Free Software
059// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
060//
061// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
062// jataylor@hairyfatguy.com
063//------------------------------------------------------------------------------
064
065public class SvgGraphics2D extends Graphics2D
066{
067   private Paint           mCurrentPaint      = sDefaultPaint;
068   private Color           mCurrentBackground = Color.BLACK;
069   private Font            mCurrentFont       = new Font("sanserif", Font.PLAIN, 12);
070   private Stroke          mCurrentStroke     = sDefaultStroke;
071   private int             mCurrentX;
072   private int             mCurrentY;
073
074   private SVG             mRootTag               = new SVG();
075   private Stack<XMLTag>   mTagStack              = new Stack<>();
076   private Stack<AffineTransform> mTransformStack = new Stack<>();
077
078
079   private static Paint    sDefaultPaint     = Color.BLACK;
080   private static Stroke   sDefaultStroke    = null; //new BasicStroke();
081   /**
082    * Used to create proper font metrics
083    */
084   private static Graphics2D        sG2D = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).createGraphics();
085
086   private static FontRenderContext sFRC;
087
088   static
089   {
090      AffineTransform frcTransform = new AffineTransform();
091      frcTransform.scale(96f/72f, 1);
092      sFRC = new FontRenderContext(frcTransform, false, false);
093   }
094
095   //##########################################################################
096   // CONSTRUCTORS
097   //##########################################################################
098
099   //--------------------------------------------------------------------------
100   public SvgGraphics2D()
101   {
102      mTagStack.push(mRootTag);
103      mTransformStack.push(new AffineTransform());
104   }
105
106   //##########################################################################
107   // PUBLIC METHODS
108   //##########################################################################
109
110   //--------------------------------------------------------------------------
111   public SVG getRootTag()
112   {
113      return mRootTag;
114   }
115
116   //--------------------------------------------------------------------------
117   public void draw(Shape shape)
118   {
119      throw new UnimplementedMethodException();
120   }
121
122   //--------------------------------------------------------------------------
123   public boolean drawImage(Image image, AffineTransform affineTransform, ImageObserver imageObserver)
124   {
125      throw new UnimplementedMethodException();
126   }
127
128   //--------------------------------------------------------------------------
129   public void drawImage(BufferedImage bufferedImage, BufferedImageOp bufferedImageOp, int i, int i1)
130   {
131      throw new UnimplementedMethodException();
132   }
133
134   //--------------------------------------------------------------------------
135   public void drawRenderedImage(RenderedImage renderedImage, AffineTransform affineTransform)
136   {
137      throw new UnimplementedMethodException();
138   }
139
140   //--------------------------------------------------------------------------
141   public void drawRenderableImage(RenderableImage renderableImage, AffineTransform affineTransform)
142   {
143      throw new UnimplementedMethodException();
144   }
145
146   //--------------------------------------------------------------------------
147   public void drawString(String inString, int x, int y)
148   {
149      drawString(inString, (float)x, (float)y);
150   }
151
152   //--------------------------------------------------------------------------
153   public void drawString(String inString, float x, float y)
154   {
155      SvgText textTag = new SvgText(inString, getFont(), new Point2D.Float(x, y));
156      textTag.addStyle("font-family:" + getFont().getFamily());
157      textTag.addStyle(CSS.fontSize(getFont().getSize()));
158      if (mCurrentPaint instanceof Color) textTag.addStyle("fill:#" + ColorUtil.colorToHex(getColor()));
159      mTagStack.peek().addSubtag(textTag);
160   }
161
162   //--------------------------------------------------------------------------
163   public void drawString(AttributedCharacterIterator attributedCharacterIterator, int i, int i1)
164   {
165      throw new UnimplementedMethodException();
166   }
167
168   //--------------------------------------------------------------------------
169   public void drawString(AttributedCharacterIterator attributedCharacterIterator, float v, float v1)
170   {
171      throw new UnimplementedMethodException();
172   }
173
174   //--------------------------------------------------------------------------
175   public boolean drawImage(Image image, int i, int i1, ImageObserver imageObserver)
176   {
177      throw new UnimplementedMethodException();
178   }
179
180   //--------------------------------------------------------------------------
181   public boolean drawImage(Image image, int i, int i1, int i2, int i3, ImageObserver imageObserver)
182   {
183      throw new UnimplementedMethodException();
184   }
185
186   //--------------------------------------------------------------------------
187   public boolean drawImage(Image image, int i, int i1, Color color, ImageObserver imageObserver)
188   {
189      throw new UnimplementedMethodException();
190   }
191
192   //--------------------------------------------------------------------------
193   public boolean drawImage(Image image, int i, int i1, int i2, int i3, Color color, ImageObserver imageObserver)
194   {
195      throw new UnimplementedMethodException();
196   }
197
198   //--------------------------------------------------------------------------
199   public boolean drawImage(Image image, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7, ImageObserver imageObserver)
200   {
201      throw new UnimplementedMethodException();
202   }
203
204   //--------------------------------------------------------------------------
205   public boolean drawImage(Image image, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7, Color color, ImageObserver imageObserver)
206   {
207      throw new UnimplementedMethodException();
208   }
209
210   //--------------------------------------------------------------------------
211   public void dispose()
212   {
213      throw new UnimplementedMethodException();
214   }
215
216   //--------------------------------------------------------------------------
217   public void drawGlyphVector(GlyphVector glyphVector, float v, float v1)
218   {
219      throw new UnimplementedMethodException();
220   }
221
222   //--------------------------------------------------------------------------
223   public void fill(Shape shape)
224   {
225      if (shape instanceof Rectangle)
226      {
227         fill((Rectangle) shape);
228      }
229      else
230      {
231         throw new UnimplementedMethodException();
232      }
233   }
234
235   //--------------------------------------------------------------------------
236   public boolean hit(Rectangle rectangle, Shape shape, boolean b)
237   {
238      throw new UnimplementedMethodException();
239   }
240
241   //--------------------------------------------------------------------------
242   public GraphicsConfiguration getDeviceConfiguration()
243   {
244      throw new UnimplementedMethodException();
245   }
246
247   //--------------------------------------------------------------------------
248   public void setComposite(Composite composite)
249   {
250      throw new UnimplementedMethodException();
251   }
252
253   //--------------------------------------------------------------------------
254   public void setRenderingHint(RenderingHints.Key key, Object object)
255   {
256      throw new UnimplementedMethodException();
257   }
258
259   //--------------------------------------------------------------------------
260   public Object getRenderingHint(RenderingHints.Key key)
261   {
262      throw new UnimplementedMethodException();
263   }
264
265   //--------------------------------------------------------------------------
266   public void setRenderingHints(Map<?, ?> map)
267   {
268      throw new UnimplementedMethodException();
269   }
270
271   //--------------------------------------------------------------------------
272   public void addRenderingHints(Map<?, ?> map)
273   {
274      throw new UnimplementedMethodException();
275   }
276
277   //--------------------------------------------------------------------------
278   public RenderingHints getRenderingHints()
279   {
280      throw new UnimplementedMethodException();
281   }
282
283   //--------------------------------------------------------------------------
284   public Graphics create()
285   {
286      throw new UnimplementedMethodException();
287   }
288
289   //--------------------------------------------------------------------------
290   public Font getFont()
291   {
292      return mCurrentFont;
293   }
294
295   //--------------------------------------------------------------------------
296   public void setFont(Font inFont)
297   {
298      mCurrentFont = inFont;
299   }
300
301   //--------------------------------------------------------------------------
302   public FontMetrics getFontMetrics(Font inFont)
303   {
304      return sG2D.getFontMetrics(inFont);
305   }
306
307   //--------------------------------------------------------------------------
308   public Rectangle getClipBounds()
309   {
310      throw new UnimplementedMethodException();
311   }
312
313   //--------------------------------------------------------------------------
314   public void clipRect(int i, int i1, int i2, int i3)
315   {
316      throw new UnimplementedMethodException();
317   }
318
319   //--------------------------------------------------------------------------
320   public void setClip(int i, int i1, int i2, int i3)
321   {
322      throw new UnimplementedMethodException();
323   }
324
325   //--------------------------------------------------------------------------
326   public Shape getClip()
327   {
328      throw new UnimplementedMethodException();
329   }
330
331   //--------------------------------------------------------------------------
332   public void setClip(Shape shape)
333   {
334      throw new UnimplementedMethodException();
335   }
336
337   //--------------------------------------------------------------------------
338   public void copyArea(int i, int i1, int i2, int i3, int i4, int i5)
339   {
340      throw new UnimplementedMethodException();
341   }
342
343   //--------------------------------------------------------------------------
344   public void drawLine(int x1, int y1, int x2, int y2)
345   {
346      if (! mCurrentStroke.equals(sDefaultStroke))
347      {
348         draw(mCurrentStroke.createStrokedShape(new Line2D.Float(x1, y1, x2, y2)));
349      }
350      else
351      {
352         SvgLine lineTag = new SvgLine(new Point(x1, y1), new Point(x2, y2));
353         if (! mCurrentPaint.equals(sDefaultPaint)) lineTag.addStyle(CSS.color(getColor()));
354         mTagStack.peek().addSubtag(lineTag);
355      }
356   }
357
358   //--------------------------------------------------------------------------
359   // A convenience method
360   public void fill(Rectangle inRect)
361   {
362      fillRect(inRect.x, inRect.y, inRect.x + inRect.width, inRect.y + inRect.height);
363   }
364
365   //--------------------------------------------------------------------------
366   public void fillRect(int x1, int y1, int x2, int y2)
367   {
368      Rectangle rect = new Rectangle(x1, y1, x2, y2);
369      if (null == mCurrentStroke
370          || mCurrentStroke instanceof BasicStroke)
371      {
372         SvgRect rectTag = new SvgRect(rect);
373         if (mCurrentPaint instanceof Color) rectTag.addStyle("fill:#" + ColorUtil.colorToHex(getColor()));
374         float lineWidth = mCurrentStroke != null ? ((BasicStroke) mCurrentStroke).getLineWidth() : 0;
375         if (lineWidth > 0)
376         {
377            rectTag.addStyle("stroke-width:" + lineWidth + "px");
378            rectTag.addStyle("stroke:#" + ColorUtil.colorToHex(mCurrentBackground));
379         }
380         mTagStack.peek().addSubtag(rectTag);
381      }
382      else
383      {
384         draw(mCurrentStroke.createStrokedShape(rect));
385      }
386
387   }
388
389   //--------------------------------------------------------------------------
390   public void clearRect(int i, int i1, int i2, int i3)
391   {
392      throw new UnimplementedMethodException();
393   }
394
395   //--------------------------------------------------------------------------
396   public void drawRoundRect(int i, int i1, int i2, int i3, int i4, int i5)
397   {
398      throw new UnimplementedMethodException();
399   }
400
401   //--------------------------------------------------------------------------
402   public void fillRoundRect(int i, int i1, int i2, int i3, int i4, int i5)
403   {
404      throw new UnimplementedMethodException();
405   }
406
407   //--------------------------------------------------------------------------
408   public void drawOval(int i, int i1, int i2, int i3)
409   {
410      throw new UnimplementedMethodException();
411   }
412
413   //--------------------------------------------------------------------------
414   public void fillOval(int i, int i1, int i2, int i3)
415   {
416      throw new UnimplementedMethodException();
417   }
418
419   //--------------------------------------------------------------------------
420   public void drawArc(int i, int i1, int i2, int i3, int i4, int i5)
421   {
422      throw new UnimplementedMethodException();
423   }
424
425   //--------------------------------------------------------------------------
426   public void fillArc(int i, int i1, int i2, int i3, int i4, int i5)
427   {
428      throw new UnimplementedMethodException();
429   }
430
431   //--------------------------------------------------------------------------
432   public void drawPolyline(int[] ints, int[] ints1, int i)
433   {
434      throw new UnimplementedMethodException();
435   }
436
437   //--------------------------------------------------------------------------
438   public void drawPolygon(int[] ints, int[] ints1, int i)
439   {
440      throw new UnimplementedMethodException();
441   }
442
443   //--------------------------------------------------------------------------
444   public void fillPolygon(int[] ints, int[] ints1, int i)
445   {
446      throw new UnimplementedMethodException();
447   }
448
449   //--------------------------------------------------------------------------
450   public void translate(int x, int y)
451   {
452      SvgGroup group = new SvgGroup();
453      group.setTransform(String.format("translate(%d,%d)", x, y));
454      mTagStack.peek().addSubtag(group);
455      mTagStack.push(group);
456      AffineTransform transform = new AffineTransform(mTransformStack.peek());
457      transform.setToTranslation(x, y);
458      mTransformStack.push(transform);
459   }
460
461   //--------------------------------------------------------------------------
462   public void translate(double x, double y)
463   {
464      SvgGroup group = new SvgGroup();
465      group.setTransform(String.format("translate(%.1f,%.1f)", x, y));
466      mTagStack.peek().addSubtag(group);
467      mTagStack.push(group);
468      AffineTransform transform = new AffineTransform(mTransformStack.peek());
469      transform.setToTranslation(x, y);
470      mTransformStack.push(transform);
471   }
472
473   //--------------------------------------------------------------------------
474   public void rotate(double v)
475   {
476      SvgGroup group = new SvgGroup();
477      group.setTransform(String.format("rotate(%.1f)", v));
478      mTagStack.peek().addSubtag(group);
479      mTagStack.push(group);
480      AffineTransform transform = new AffineTransform(mTransformStack.peek());
481      transform.setToRotation(v);
482      mTransformStack.push(transform);
483   }
484
485   //--------------------------------------------------------------------------
486   public void rotate(double v, double v1, double v2)
487   {
488      SvgGroup group = new SvgGroup();
489      group.setTransform(String.format("rotate(%.1f, %.1f, %.1f)", v, v1, v2));
490      mTagStack.peek().addSubtag(group);
491      mTagStack.push(group);
492      AffineTransform transform = new AffineTransform(mTransformStack.peek());
493      transform.setToRotation(v, v1, v2);
494      mTransformStack.push(transform);
495   }
496
497   //--------------------------------------------------------------------------
498   public void scale(double v, double v1)
499   {
500      throw new UnimplementedMethodException();
501   }
502
503   //--------------------------------------------------------------------------
504   public void shear(double v, double v1)
505   {
506      throw new UnimplementedMethodException();
507   }
508
509   //--------------------------------------------------------------------------
510   public void transform(AffineTransform inAffineTransform)
511   {
512      AffineTransform transform = new AffineTransform(mTransformStack.peek());
513      transform.concatenate(inAffineTransform);
514      mTransformStack.push(transform);
515   }
516
517   //--------------------------------------------------------------------------
518   public void setTransform(AffineTransform inAffineTransform)
519   {
520      while (mTagStack.size() > 1 && ! inAffineTransform.equals(mTransformStack.peek()))
521      {
522         mTagStack.pop();
523         mTransformStack.pop();
524      }
525
526      if (! inAffineTransform.equals(mTransformStack.peek()))
527      {
528         mTransformStack.pop();
529         mTransformStack.push(new AffineTransform(inAffineTransform));
530      }
531   }
532
533   //--------------------------------------------------------------------------
534   public AffineTransform getTransform()
535   {
536      return new AffineTransform(mTransformStack.peek()); // Return a copy
537   }
538
539   //--------------------------------------------------------------------------
540   public void setPaint(Paint inPaint)
541   {
542      mCurrentPaint = inPaint;
543   }
544
545   //--------------------------------------------------------------------------
546   public Paint getPaint()
547   {
548      return mCurrentPaint;
549   }
550
551   //--------------------------------------------------------------------------
552   public Color getColor()
553   {
554      return (mCurrentPaint instanceof Color ? (Color) mCurrentPaint : null);
555   }
556
557   //--------------------------------------------------------------------------
558   public void setColor(Color inColor)
559   {
560      mCurrentPaint = inColor;
561   }
562
563   //--------------------------------------------------------------------------
564   /**
565    Sets the paint mode of this graphics context to overwrite the destination
566    with this graphics context's current color. This sets the logical pixel
567    operation function to the paint or overwrite mode. All subsequent rendering
568    operations will overwrite the destination with the current color.
569    */
570   public void setPaintMode()
571   {
572      throw new UnimplementedMethodException();
573   }
574
575   //--------------------------------------------------------------------------
576   /**
577    <b>Not implemented</b>.
578    <p>
579    Sets the paint mode of this graphics context to alternate between this
580    graphics context's current color and the new specified color. This specifies
581    that logical pixel operations are performed in the XOR mode, which alternates
582    pixels between the current color and a specified XOR color.
583    </p>
584    <p>
585    When drawing operations are performed, pixels which are the current color are
586    changed to the specified color, and vice versa.
587    </p>
588    <p>
589    Pixels that are of colors other than those two colors are changed in an
590    unpredictable but reversible manner; if the same figure is drawn twice, then
591    all pixels are restored to their original values.
592    </p>
593    @param inColor the color to alternate with the current default color of the graphics context
594    */
595   public void setXORMode(Color inColor)
596   {
597      throw new UnimplementedMethodException();
598   }
599
600   //--------------------------------------------------------------------------
601   public Composite getComposite()
602   {
603      throw new UnimplementedMethodException();
604   }
605
606   //--------------------------------------------------------------------------
607   public void setBackground(Color inValue)
608   {
609      mCurrentBackground = inValue;
610   }
611
612   //--------------------------------------------------------------------------
613   public Color getBackground()
614   {
615      return mCurrentBackground;
616   }
617
618   //--------------------------------------------------------------------------
619   public void setStroke(Stroke inStroke)
620   {
621      mCurrentStroke = inStroke;
622   }
623
624   //--------------------------------------------------------------------------
625   public Stroke getStroke()
626   {
627      return mCurrentStroke;
628   }
629
630   //--------------------------------------------------------------------------
631   public void clip(Shape shape)
632   {
633      throw new UnimplementedMethodException();
634   }
635
636   //--------------------------------------------------------------------------
637   public FontRenderContext getFontRenderContext()
638   {
639      return sFRC;
640   }
641
642}