Skip To Content ArcGIS for Developers Sign In Dashboard

ArcGIS Runtime SDK for Java

Map printing

Download Sample Viewer

Description

This application gives an example of how to use the Printing functionality of the JMap. The JMap implements the Pageable interface, allowing a map component to be passed to a PrinterJob job as its pageable object. The print job can then be sent to an available printer. To use the sample, pan, zoom, and resize the window to get a desired printing map extent on screen, then choose the desired printing options via the options panel. Based on the settings chosen, the map calculates the number of pages required to print itself out without stretching or shrinking itself; this is displayed on screen to the user. Click the 'print' button to send the job to the selected printer.

Code snippet


  // Create a printer job and set the map as the pageable and call print on it
  PrinterJob printJob = PrinterJob.getPrinterJob();
  printJob.setPageable(map);

  // set chosen printer
  printJob.setPrintService(...);
  // send job to printer!
  printJob.print();
  

Sample Code

/* Copyright 2014 Esri

All rights reserved under the copyright laws of the United States
and applicable international laws, treaties, and conventions.

You may freely redistribute and use this sample code, with or
without modification, provided you include the original copyright
notice and use restrictions.

See the use restrictions.*/
package com.esri.client.samples.map;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.print.PrintService;
import javax.print.attribute.Size2DSyntax;
import javax.print.attribute.standard.MediaSize;
import javax.print.attribute.standard.MediaSizeName;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.print.PageFormat;
import java.awt.print.Pageable;
import java.awt.print.Paper;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.util.HashMap;
import java.util.LinkedHashMap;

import com.esri.runtime.ArcGISRuntime;
import com.esri.toolkit.overlays.ScaleBarOverlay;
import com.esri.core.geometry.Envelope;
import com.esri.map.ArcGISTiledMapServiceLayer;
import com.esri.map.JMap;
import com.esri.map.MapOverlay;

/***
 * This application gives an example of how to use the Printing functionality
 * of the {@link JMap}. The JMap implements the {@link Pageable} interface,
 * allowing a map component to be passed to a {@link PrinterJob} job as its
 * pageable object.  The print job can then be sent to an available printer.
 * <p>
 * To use the sample, pan, zoom, and resize the window to get a desired printing
 * map extent on screen, then choose the desired printing options via the options
 * panel. Based on the settings chosen, the map calculates the number of pages
 * required to print itself out without stretching or shrinking itself; this is
 * displayed on screen to the user. Click the 'print' button to send the job to
 * the selected printer.
 */
public class PrintingApp implements ActionListener {

  // components
  private JMap map;
  private ScaleBarOverlay scaleBar;
  private JPanel userPanel;
  private JLayeredPane contentPane;
  private JComboBox<String> orientationList;
  private JComboBox<String> printerList;
  private JComboBox<String> sizeList;
  private JLabel pages;

  // for combo boxes
  private HashMap<String, Integer> orientationMap;
  private HashMap<String, MediaSize> sizeMap;
  private HashMap<String, PrintService> printerMap;
  private PageDrawOverlay pageDrawOverlay;

  // constants/other
  private static MediaSize letter = MediaSize.getMediaSizeForName(MediaSizeName.NA_LETTER);
  private static MediaSize legal = MediaSize.getMediaSizeForName(MediaSizeName.NA_LEGAL);
  private static MediaSize a4 = MediaSize.getMediaSizeForName(MediaSizeName.ISO_A4);
  private static MediaSize a5 = MediaSize.getMediaSizeForName(MediaSizeName.ISO_A5);
  private static PrintService[] printServices = PrinterJob.lookupPrintServices();
  private static double MARGIN = 12; // set a (12/72) inch margin on all sides
  private static final int COMPONENT_HEIGHT = 25;
  private static final int COMPONENT_WIDTH = 220;

  // ------------------------------------------------------------------------
  // Constructor
  // ------------------------------------------------------------------------
  public PrintingApp() {
  }

  // ------------------------------------------------------------------------
  // Core Functionality
  // ------------------------------------------------------------------------
  @Override
  public void actionPerformed(ActionEvent arg0) {
    setPrintSettings();
  }

  private void setPrintSettings() {
    // set up paper options
    Paper paper = new Paper();
    MediaSize size = sizeMap.get(sizeList.getSelectedItem());
    double width = (size.getX(Size2DSyntax.INCH))*72;
    double height = (size.getY(Size2DSyntax.INCH))*72;
    paper.setSize(width, height);
    paper.setImageableArea(MARGIN, MARGIN, width - (2*MARGIN), height - (2*MARGIN)); // set even margins

    // set up page format options
    PageFormat page = new PageFormat();
    page.setOrientation(orientationMap.get(orientationList.getSelectedItem()).intValue());
    page.setPaper(paper);

    // set the page format into the map: this triggers pagination calculations
    map.setPageFormat(page);
    int numPages = map.getNumberOfPages();
    pages.setText("Print job requires " + numPages + " page" + (numPages == 1? "":"s"));

    pageDrawOverlay.setPages(page);
  }

  private void onPrint() {
    // Create a printer job and set the map as the pageable and call print on it
    PrinterJob printJob = PrinterJob.getPrinterJob();
    printJob.setPageable(map);
    try {
      // set chosen printer
      printJob.setPrintService(printerMap.get(printerList.getSelectedItem()));
      // send job to printer!
      printJob.print();
    } catch (PrinterException e) {
      JOptionPane.showMessageDialog(contentPane.getParent(),
          wrap("An error has occured. " + e.getLocalizedMessage()), "", JOptionPane.ERROR_MESSAGE);
      e.printStackTrace();
    }
  }

  // ------------------------------------------------------------------------
  // Static methods
  // ------------------------------------------------------------------------
  /**
   * Starting point of this application.
   * @param args arguments to this application.
   */
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        try {
          PrintingApp app = new PrintingApp();
          JFrame appWindow = app.createWindow();
          appWindow.add(app.createUI());
          appWindow.setVisible(true);
        }
        catch (Exception e) {
          e.printStackTrace();
        }
      }
    });
  }

  // ------------------------------------------------------------------------
  // Public methods
  // ------------------------------------------------------------------------
  /**
   * Creates and displays the UI, including the map, for this application.
   */
  public JComponent createUI() {

    map = new JMap();
    map.getLayers().add(new ArcGISTiledMapServiceLayer(
        "http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer"));

    // set an initial extent
    map.setExtent(new Envelope(-1545662, 4100767, 651245, 5534561.95));

    // recalculate and display number of pages when JMap size changes
    map.addComponentListener(new ComponentAdapter() {

      @Override
      public void componentResized(ComponentEvent arg0) {
        SwingUtilities.invokeLater(new Runnable() {
          @Override
          public void run() {
            setPrintSettings();
          }
        });
      }
    });

    scaleBar = new ScaleBarOverlay();
    scaleBar.setActive(false);
    map.addMapOverlay(scaleBar);

    pageDrawOverlay = new PageDrawOverlay();
    pageDrawOverlay.setActive(false);
    map.addMapOverlay(pageDrawOverlay);

    contentPane = createContentPane();

    // input panel
    userPanel = createUserPanel();
    userPanel.setVisible(true);
    userPanel.setLocation(10, 10);

    contentPane.add(userPanel);
    contentPane.add(map);

    return contentPane;
  }

  // ------------------------------------------------------------------------
  // Private methods
  // ------------------------------------------------------------------------
  /**
   * Creates the user panel
   */
  private JPanel createUserPanel() {
    JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
    panel.setSize(COMPONENT_WIDTH, 310);
    panel.setLocation(10, 10);

    JLabel printLabel = new JLabel("Choose a printer:");
    printLabel.setForeground(Color.WHITE);
    printLabel.setAlignmentX(Component.LEFT_ALIGNMENT);

    printerMap = new HashMap<>();
    for (PrintService printer : printServices) {
      printerMap.put(printer.getName(), printer);
    }

    printerList = new JComboBox<>(printerMap.keySet().toArray(new String[0]));
    printerList.setMaximumSize(new Dimension(COMPONENT_WIDTH, COMPONENT_HEIGHT));
    printerList.setAlignmentX(Component.LEFT_ALIGNMENT);
    printerList.addActionListener(this);

    orientationMap = new HashMap<>();
    orientationMap.put("Portrait", Integer.valueOf(PageFormat.PORTRAIT));
    orientationMap.put("Landscape", Integer.valueOf(PageFormat.LANDSCAPE));
    orientationMap.put("Reverse Landscape", Integer.valueOf(PageFormat.REVERSE_LANDSCAPE));

    JLabel orientations = new JLabel("Choose page orientation:");
    orientations.setForeground(Color.WHITE);
    orientations.setAlignmentX(Component.LEFT_ALIGNMENT);
    orientationList = new JComboBox<>(new String[]{"Portrait", "Landscape", "Reverse Landscape"});
    orientationList.setMaximumSize(new Dimension(COMPONENT_WIDTH, COMPONENT_HEIGHT));
    orientationList.setAlignmentX(Component.LEFT_ALIGNMENT);
    orientationList.addActionListener(this);

    sizeMap = new LinkedHashMap<>();
    sizeMap.put("US Letter (8.5 x 11 in)", letter);
    sizeMap.put("US Legal (8.5 x 14 in)", legal);
    sizeMap.put("A4 International (210 x 297 mm)", a4);
    sizeMap.put("A5 International (148 x 210 mm)", a5);

    JLabel sizes = new JLabel("Choose paper size:");
    sizes.setForeground(Color.WHITE);
    sizes.setAlignmentX(Component.LEFT_ALIGNMENT);
    sizeList = new JComboBox<>(sizeMap.keySet().toArray(new String[0]));
    sizeList.setMaximumSize(new Dimension(COMPONENT_WIDTH, COMPONENT_HEIGHT));
    sizeList.setAlignmentX(Component.LEFT_ALIGNMENT);
    sizeList.addActionListener(this);

    JCheckBox scaleCheckBox = new JCheckBox(" Add a scale bar");
    scaleCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
    scaleCheckBox.setForeground(Color.WHITE);
    scaleCheckBox.setBackground(Color.DARK_GRAY);
    scaleCheckBox.setSelected(false);
    scaleCheckBox.addItemListener(new ItemListener() {

      @Override
      public void itemStateChanged(ItemEvent arg0) {
        if (scaleBar != null) {
          if (arg0.getStateChange() == ItemEvent.DESELECTED) {
            scaleBar.setActive(false);
          } else if (arg0.getStateChange() == ItemEvent.SELECTED) {
            scaleBar.setActive(true);
          }
        }
      }
    });

    JCheckBox pageCheckBox = new JCheckBox(" Show page outlines");
    pageCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
    pageCheckBox.setForeground(Color.WHITE);
    pageCheckBox.setBackground(Color.DARK_GRAY);
    pageCheckBox.setSelected(false);
    pageCheckBox.addItemListener(new ItemListener() {

      @Override
      public void itemStateChanged(ItemEvent arg0) {
        if (scaleBar != null) {
          if (arg0.getStateChange() == ItemEvent.DESELECTED) {
            pageDrawOverlay.setActive(false);
          } else if (arg0.getStateChange() == ItemEvent.SELECTED) {
            pageDrawOverlay.setActive(true);
          }
        }
      }
    });

    pages = new JLabel("");
    pages.setFont(new Font(pages.getFont().getFontName(), Font.ITALIC, pages.getFont().getSize()));
    pages.setForeground(Color.WHITE);
    pages.setAlignmentX(Component.LEFT_ALIGNMENT);

    // layout all the components together into a panel
    panel.setBackground(Color.DARK_GRAY);
    panel.add(printLabel);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(printerList);
    panel.add(Box.createRigidArea(new Dimension(0,15)));
    panel.add(orientations);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(orientationList);
    panel.add(Box.createRigidArea(new Dimension(0,15)));
    panel.add(sizes);
    panel.add(Box.createRigidArea(new Dimension(0,5)));
    panel.add(sizeList);
    panel.add(Box.createRigidArea(new Dimension(0,10)));
    panel.add(scaleCheckBox);
    panel.add(Box.createRigidArea(new Dimension(0,10)));
    panel.add(pageCheckBox);
    panel.add(Box.createRigidArea(new Dimension(0,10)));
    panel.add(pages);
    panel.add(Box.createRigidArea(new Dimension(0,10)));
    panel.add(createPrintButton());
    panel.setBorder(BorderFactory.createEmptyBorder(5,10,5,10));
    return panel;
  }

  private JButton createPrintButton() {
    JButton button = new JButton("Print map");
    button.setMaximumSize(new Dimension(COMPONENT_WIDTH, 30));
    button.setMinimumSize(new Dimension(COMPONENT_WIDTH, 30));
    button.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent arg0) {
        // so we don't print the page outlines
        pageDrawOverlay.setActive(false);
        map.repaint();
        // print!
        onPrint();
        pageDrawOverlay.setActive(true);
      }
    });
    return button;
  }

  /**
   * Creates a window.
   * @return a window.
   */
  private JFrame createWindow() {
    JFrame window = new JFrame("Printing application");
    window.setBounds(100, 100, 1000, 700);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.getContentPane().setLayout(new BorderLayout(0, 0));
    window.addWindowListener(new WindowAdapter() {
      @Override
      public void windowClosing(WindowEvent windowEvent) {
        super.windowClosing(windowEvent);
        map.dispose();
      }
    });
    return window;
  }

  /**
   * Creates a content pane.
   * @return a content pane.
   */
  private static JLayeredPane createContentPane() {
    JLayeredPane contentPane = new JLayeredPane();
    contentPane.setLayout(new BorderLayout(0, 0));
    contentPane.setVisible(true);
    return contentPane;
  }

  public class PageDrawOverlay extends MapOverlay {

    private static final long serialVersionUID = 1L;
    private Color outlineColor = new Color(200, 0, 0);
    private int width = 0;
    private int height = 0;
    private int numPagesX = 0;
    private int numPagesY  = 0;
    private double ratio = 72.0 / ArcGISRuntime.getDPI();

    public PageDrawOverlay(){
    }

    private void setPages(PageFormat pf) {
      // in map units
      width = (int) (pf.getImageableWidth()/ratio);
      height = (int) (pf.getImageableHeight()/ratio);

      // total printer width / printer width of one page
      numPagesX = (int) Math.ceil((map.getWidth() * ratio)/pf.getImageableWidth());
      numPagesY = (int) Math.ceil((map.getHeight() * ratio)/pf.getImageableHeight());

      getMap().repaint();
    }

    @Override
    public void paint(Graphics graphics) {
      if (isActive()) {
        for (int pX = 0; pX < numPagesX; pX++) {
          for (int pY = 0; pY < numPagesY; pY++) {
            // draw a page rectangle
            Graphics2D g = (Graphics2D) graphics;
            g.setColor(outlineColor);
            g.drawRect(pX*width, pY*height, width, height);
          }
        }
      }
    }
  }
  
  private String wrap(String str) {
    // create a HTML string that wraps text when longer
    return "<html><p style='width:200px;'>" + str + "</html>";
  }
}
Feedback on this topic?