2011년 6월 9일 목요일

JDK 1.4의 프린팅 (printing), Part 2

프린트 관련 이벤트를 듣고 그래픽을 직접 프린트하는 방법

John Zukowski
회장 (JZ Ventures, Inc.)
2002년 4월

새로운 Java Print Service API 소개, 두 번째 시간이다. John Zukowski는 프린트 작동을 위해 이벤트 리스너(event listener)를 설정하는 방법, 스크린이나 컴포넌트의 내용을 프린트하는 방법, 프린팅 작동에서 sleep() 호출을 제거하는 방법을 설명한다.
Part 1에서는 파일 시스템에서 직접 프린팅하는 방법과 프린터 다이얼로그를 이용하여 프린터 선택을 프롬프트하는 방법을 배웠다. 이번 주에는, 이벤트 리스너(event listener)를 프린트 작동에 적용하고 스크린이나 컴포넌트에서 직접 프린트하는 방법을 설명하겠다. 이전 글과 마찬가지로 새로운 프린트 API를 경험할 수 있도록 실행 예제로 마무리할 것이다. Print Service API에 익숙하지 않다면 이전 글을 읽기 바란다.
프린팅 이벤트 듣기(listen)
Part 1의 예제 프로그램의 마지막 라인이다:
Thread.sleep(10000);


이러한 호출로 다른 쓰레드가 프로그램에서 프린터로 데이터를 전송하는 동안 주 프로그램은 10초 동안 작동을 멈춘다(sleep). 논리적으로 전송률을 고려해볼 때, 대부분의 이미지 파일은 계획된 시간에 전송을 끝마쳐야 한다. 전송은 10초 이내에 완료되어야 할 것이다. 일반적으로, 전송은 빠르기 때문에 최적의 퍼포먼스를 위해 정지 시간(sleep time)을 조작할 수 있다. 하지만 최상의 시간을 간단히 계산할 수는 없다.
Thread.sleep()에 의존을 원하지 않거나 전송이 완료될 때 계산하기를 원하지 않는 사람들에게 Java Print Service API는 PrintJobListener 형식의 대안을 제공한다. Listing 1은 인터페이스이다:
Listing 1. PrintJobListener 인터페이스
  public interface PrintJobListener {
    public void printDataTransferCompleted(PrintJobEvent e);
    public void printJobCompleted(PrintJobEvent e);
    public void printJobFailed(PrintJobEvent e);
    public void printJobCanceled(PrintJobEvent e);
    public void printJobNoMoreEvents(PrintJobEvent e);
    public void printJobRequiresAttention(PrintJobEvent e);
  }


PrintJobListener 인터페이스에는 6개의 메소드가 있다. PrintJobListener는 다른 "delegation-based" 이벤트 리스너 처럼 작동한다: 일단, 리스너를 만들어서 특정 이벤트에 이를 등록하면 해당 이벤트가 발생할 때 알려질 것이다.
자신만의 인터페이스의 메소드를 구현할 옵션을 갖게 되고 Print Service API 에는 여러분을 위해서 무엇이든 하는 어댑터 클래스가 있다. 여러분이 해야 할 일은 PrintJobAdapter를 분류하고 관심 있는 Notification용 메소드를 구현하는 것이다.
예를 들어, 전송이 완료되는 대로 프로그램을 정지시키려면 printDataTransferCompleted()의 System.exit()에 호출을 추가하면 된다. (Listing 2):
Listing 2. PrintJobListener 등록하기
  PrintService printService =
    PrintServiceLookup.lookupDefaultPrintService();
  DocPrintJob job = printService.createPrintJob();
  PrintJobListener listener = new PrintJobAdapter() {
    public void printDataTransferCompleted(PrintJobEvent e) {
      System.out.println("Good-bye");
      System.exit(0);
    }
  };
  job.addPrintJobListener(listener);


PrintJobListener가 작동을 훌륭히 수행하는 동안 시스템은 전송이 발생할 때까지 기다려야 한다. 프린터 선택 다이얼로그나 다른 GUI 관련 태스크를 보여주지 않으면 프로그램은 즉시 종료된다. 프린터로의 데이터 전송은 데몬 쓰레드에서 발생하며 자바 런타임은 전송이 진행되는 동안 종료할 수 있다. 어떤 GUI 관련 태스크를 만든다는 것은 비 데몬(non-daemon) AWT 쓰레드를 만드는 것이다. 이것은 데이터가 전송되는 동안 시스템을 지속시켜준다.
그래픽 그리기
이전 글에서 디스크에서 파일을 프린트하는 방법을 배웠다. 메모리 내부의 이미지 또는 컴포넌트의 내용을 프린팅하는 것은 약간 다르다. 다음은 DocFlavor.SERVICE_FORMATTED가 작동하는 장소이다.
DocFlavor.SERVICE_FORMATTED는 세 개의 인터페이스와 함께 작동한다:
  • java.awt.print.Pageable
  • java.awt.print.Printable
  • java.awt.image.renderable.RenderableImage

지난 글에서 SimpleDoc를 만들어서, 프린트를 위해 Doc을 정의할 때, 디스크 파일용으로 InputStream 에서 전달했다(Listing 3).
Listing 3. 콘텐트 설정하기
  DocFlavor flavor = DocFlavor.INPUT_STREAM.PNG;
  String filename = ...;
  FileInputStream fis = new FileInputSteam(filename);
  DocAttributeSet das = new HashDocAttributeSet();
  Doc doc = new SimpleDoc(fis, flavor, das);


DocFlavor.SERVICE_FORMATTED를 이용하여 작동할 때, 메모리에서 그래픽을 프린트하려면 이들 인터페이스 중 하나의 구현자에서 전달해야 한다.
java.awt.print.Printable 인터페이스를 구현하는 컴포넌트를 정의해보자(Listing 4)..
Listing 4. Printable 인터페이스
  public interface Printable {
    public static final int PAGE_EXISTS;
    public static final int NO_SUCH_PAGE;
    public int print(Graphics g, PageFormat pf, int page)
      throws PrinterException;
  }


Printable 의 print() 메소드에서 paint()를 호출하는 컴포넌트를 만들어서 Printable 인터페이스를 구현했다. Listing 5는 일반적인 구현이다.
Listing 5. Printable 구현
  public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
    int x = (int)pageFormat.getImageableX();
    int y = (int)pageFormat.getImageableY();
    g.translate(x, y);
    if (pageIndex == 0) {
      paint(g);
      return Printable.PAGE_EXISTS;
    } else {
      return Printable.NO_SUCH_PAGE;
    }
  }


한 페이지 정도의 길이라면 모든 컴포넌트를 위해 이러한 구현을 사용할 수 있다. 메소드의 첫 번째 부분은 "드로잉(drawing) 부분을 프린터에서 사용되는 구역으로 옮겨라." 라고 명령한다. 두 번째 부분은, "첫 번째 페이지에서 paint()를 호출하고 다른 페이지의 경우 호출하지 말라."고 명령하고 있다. 이는 프린트하려고 할 때 paint() 로의 단일 호출이라는 결과를 만든다.
예제
이전 글에서 새로운 프린트 기능을 시도할 예제를 보여주었다. Listing 6은 지금까지 작동한 모든 코드를 조합한 것이다. 아래의 컴포넌트에는 스크린 중앙에 커스텀 "Hello, Printer" 그리고 주위에 박스를 그린다. Print 버튼을 누르면 아웃풋이 디폴트 프린터로 전송되고 프로그램은 종료한다.
Listing 6. 프린팅 예제
import javax.print.*;
import javax.print.event.*;
import javax.print.attribute.*;
import java.awt.print.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;

public class PrintPrintable {

  static class MyComponent extends JPanel
      implements Printable {

    Font theFont = new Font("Serif", Font.ITALIC, 48);

    public void paint(Graphics g) {
      super.paint(g);
      String msg = "Hello, Printer";

      g.setFont(theFont);
      FontMetrics fm = g.getFontMetrics();

      // Center line
      int width = getWidth();
      int stringWidth = fm.stringWidth(msg);
      int x = (width - stringWidth)/2;

      int height = getHeight();
      int stringHeight = fm.getHeight();
      int ascent  = fm.getAscent();
      int y = (height - stringHeight)/2 + ascent;

      g.drawString(msg, x, y);
      g.drawRect(x, y-ascent, stringWidth, stringHeight);

    }

    public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
      int x = (int)pageFormat.getImageableX();
      int y = (int)pageFormat.getImageableY();
      g.translate(x, y);
      if (pageIndex == 0) {
        paint(g);
        return Printable.PAGE_EXISTS;
      } else {
        return Printable.NO_SUCH_PAGE;
      }
    }
  }

  public static void main(String args[]) throws Exception {

    final JFrame frame = new JFrame("Printing Graphics");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    Container contentPane = frame.getContentPane();

    final Component printIt = new MyComponent();
    contentPane.add(printIt, BorderLayout.CENTER);

    JButton button = new JButton("Print");
    contentPane.add(button, BorderLayout.SOUTH);

    ActionListener listener = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
        PrintService printService =
          PrintServiceLookup.lookupDefaultPrintService();
        DocPrintJob job = printService.createPrintJob();
        PrintJobListener pjlistener = new PrintJobAdapter() {
          public void printDataTransferCompleted(PrintJobEvent e) {
            System.out.println("Good-bye");
            System.exit(0);
          }
        };
        job.addPrintJobListener(pjlistener);

        PrintRequestAttributeSet pras =
          new HashPrintRequestAttributeSet();

        DocAttributeSet das = new HashDocAttributeSet();
        Doc doc = new SimpleDoc(printIt, flavor, das);
        try {
          job.print(doc, pras);
        } catch (PrintException pe) {
          pe.printStackTrace();
        }
      }
    };
    button.addActionListener(listener);

    frame.setSize(350, 250);
    frame.show();
  }
}

댓글 없음:

댓글 쓰기

블록체인 개요 및 오픈소스 동향

블록체인(block chain) 블록체인은 공공 거래장부이며 가상 화폐로 거래할때 발생할때 발생할 수 있는 해킹을 막는 기술. 분산 데이터베이스의 한 형태로, 지속적으로 성장하는 데이터 기록 리스트로서 분산 노드의 운영자에 의한 임의 조작이 불가...