When you need to produce lots of fairly straightforward graphs, Java2D is your friend. In this blog, I show you how you can render simple images as crisp-looking PDF or EPS files, provided you can draw them on a Graphics2D object.
Recently, I had to draw a bunch of simple images, such as this one
or this PDF
Of course, you can do these drawings in Illustrator or Inkscape. But if you are a coder like me, a graphics program may not be your cup of tea. It takes me about 15 seconds to code
for (int i = 0; i < 6; i++) for (int j = 0; j < 6; j++) { g2.setColor(2 <= i && i <= 3 && 2 <= j && j <= 3 ? Color.GREEN : Color.RED); g2.fillRect(i * 30, j * 30, 29, 29); }
(Or at least it feels like 15 seconds :-))
To do the same in Inkscape would take me a seriously long time, and I would be bored to tears.
What about that maze? I had to set it up anyway for a CS1 lab, using Alice, and it wasn't much extra trouble to add a
draw
(Graphics2D g2)
method to each
Robot
, Wall
, and Beeper
object. I really
enjoyed having an image with perfect lines and circles, with only a few minutes
of work.
Once you can draw your image on a Graphics2D
, it is a routine
matter to turn it into a PostScript or image file, using standard Java 2D
operations. You can then process the PostScript output into PDF or EPS using
ps2pdf
or ps2eps
. Java 2D seems to have fallen out of
fashion, and there doesn't seem to be a lot of useful information (outside Core Java, Filthy Rich Clients, and the Sun Java
tutorial). That's too bad—it is a very powerful framework and it
isn't hard to learn. So, here is the program outline that I use. Feel free to
use it for your 2D rendering needs.
import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.awt.print.*; import java.io.*; import javax.imageio.*; import javax.print.*; import javax.print.attribute.*; import javax.swing.*; public class Image { // Set your image dimensions here private static int IMAGE_WIDTH = ...; private static int IMAGE_HEIGHT = ...; public static void draw(Graphics2D g2) { // Your drawing instructions go here ... } // No need to touch anything below this line public static void main(String[] args) { final JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JComponent component = new JComponent() { public void paintComponent(Graphics g) { draw((Graphics2D) g); } public Dimension getPreferredSize() { return new Dimension(IMAGE_WIDTH, IMAGE_HEIGHT); } }; frame.add(component); JMenuBar menuBar = new JMenuBar(); frame.setJMenuBar(menuBar); JMenu menu = new JMenu("File"); menu.setMnemonic('F'); menuBar.add(menu); JMenuItem item = new JMenuItem("Save", 'S'); menu.add(item); item.setAccelerator(KeyStroke.getKeyStroke("ctrl S")); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JFileChooser chooser = new JFileChooser(); if (chooser.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) try { saveImage(component, chooser.getSelectedFile().getPath()); } catch (Exception ex) { ex.printStackTrace(); } } }); frame.pack(); frame.setVisible(true); } private static void saveImage(final JComponent comp, String fileName) throws IOException, PrintException { if (fileName.endsWith(".ps")) { DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE; String mimeType = "application/postscript"; StreamPrintServiceFactory[] factories = StreamPrintServiceFactory.lookupStreamPrintServiceFactories(flavor, mimeType); FileOutputStream out = new FileOutputStream(fileName); if (factories.length > 0) { PrintService service = factories[0].getPrintService(out); SimpleDoc doc = new SimpleDoc(new Printable() { public int print(Graphics g, PageFormat pf, int page) { if (page >= 1) return Printable.NO_SUCH_PAGE; else { double sf1 = pf.getImageableWidth() / (comp.getWidth() + 1); double sf2 = pf.getImageableHeight() / (comp.getHeight() + 1); double s = Math.min(sf1, sf2); Graphics2D g2 = (Graphics2D) g; g2.translate((pf.getWidth() - pf.getImageableWidth()) / 2, (pf.getHeight() - pf.getImageableHeight()) / 2); g2.scale(s, s); comp.paint(g); return Printable.PAGE_EXISTS; } } }, flavor, null); DocPrintJob job = service.createPrintJob(); PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet(); job.print(doc, attributes); } } else { Rectangle rect = comp.getBounds(); BufferedImage image = new BufferedImage(rect.width, rect.height, BufferedImage.TYPE_INT_RGB); Graphics2D g = (Graphics2D) image.getGraphics(); g.setColor(Color.WHITE); g.fill(rect); g.setColor(Color.BLACK); comp.paint(g); String extension = fileName.substring(fileName.lastIndexOf('.') + 1); ImageIO.write(image, extension, new File(fileName)); g.dispose(); } } }