IBM Korea Skip to main content
       IBM 홈    |  제품 & 서비스  |  고객지원 & 다운로드  |  회원가입  

Magic with Merlin: JDK 1.4의 프린팅 (printing), Part 2
목 차:
프린팅 이벤트 듣기(listen)
그래픽 그리기
예제
참고 자료
필자 소개
기사에 대한 평가
관련 dW 링크:
Java event delivery techniques
Printing in JDK 1.4, Part 1
Merlin's complete archive
Subscribe to the developerWorks newsletter
US 원문 읽기
Also in the Java zone:
Tutorials
Tools and products
Code and components
Articles
 
프린트 관련 이벤트를 듣고 그래픽을 직접 프린트하는 방법


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

Column icon새로운 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;
   }

Printableprint() 메소드에서 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();
   }
}

다음 Magic with Merlin 칼럼에서는 Swing의 새로운 JFormattedTextField 컴포넌트를 연구하겠다.

참고자료

필자소개
Photo of John Zukowski John Zukowski는 JZ Ventures, Inc. 에서 전략적 자바 컨설팅을 수행하고 있으며 jGuru 커뮤니티에서 운영하는 Java FAQs의 구루(guru)로 활동하고 있다. 저서로는 Mastering Java 2, J2SE 1.4Learn Java with JBuilder 6 등이 있다.



이 기사에 대하여 어떻게 생각하십니까?

정말 좋다 (5) 좋다 (4) 그저그렇다 (3) 수정보완이 필요하다(2) 형편없다 (1)

  회사소개  |  개인보호정책  |  법률  |  문의