Java印刷プログラム設計


[size=medium]1はじめに
私たちの実際の仕事では、印刷機能を実現する必要があることがよくあります.しかし、歴史的な理由でJavaが提供する印刷機能はずっと弱い.実際には、最初のjdkは印刷をまったくサポートしておらず、jdk 1.1まで軽量な印刷サポートが導入された.従って、従来Java/APplet/JSP/サーブレットで設計されたプログラムでは、複雑な印刷はActiveX/OCXコントロールまたはVB/VCプログラムを呼び出すことによって実現されており、非常に面倒である.実際、SUN社もJava印刷機能の完備に力を入れてきたが、Java 2プラットフォームはついに頑丈な印刷モードの始まりがあり、この印刷モードはJava 2 Dグラフィックパッケージと十分に結合している.さらに鼓舞的なことに、新しく発表されたjdk 1.4は、既存の印刷機能を積極的に補完する完全な「Java印刷サービスAPI」(Java Print Service API)を提供しています.これにより、印刷文字、グラフィック、ファイル、印刷プレビューなど、実用的なアプリケーションのニーズの大部分を実現できます.本稿では、Java印刷プログラムを設計してこれらの機能を実現する方法を具体的なプログラム例で説明し、異なるバージョンの実現方法を分析比較し、有益なヒントを得ることを望んでいます.
2 Javaでの印刷
2.1 Javaの印刷API
Javaの印刷APIは主にjava.awt.printパッケージに存在する.jdk 1.4の新しいクラスは主にjavax.printパッケージと対応するサブパッケージjavax.print.eventとjavax.print.attributeに存在します.ここでjavax.printパッケージには主に印刷サービスに関するクラスが含まれ、javax.print.eventには印刷イベントに関する定義が含まれ、javax.print.attributeには印刷サービスの利用可能な属性リストなどが含まれます.
2.2印刷方法
1つの印刷を作成するには、少なくとも2つのことを考慮する必要があります.
印刷サービスオブジェクトが必要です.これは、jdk 1.4以前のバージョンではjava.awt.print.Printableインタフェースを実装するか、Toolkit.getDefaultToolkit().getPrintJobを介して印刷サービスオブジェクトを取得する必要があります.jdk 1.4では、javax.print.PrintSerivceLookupを使用して印刷サービスオブジェクトを検索できます.
印刷作業を開始する必要があります.これには、jdk 1.4の前にjava.awt.print.PrintJob(jdk 1.1で提供され、現在はほとんど使われていない)を呼び出してprintまたはprintAllメソッドを呼び出して印刷を開始する方法もいくつかあります.java.awt.print.PrinterJobのprintDialogで印刷ダイアログボックスを表示し、printメソッドで印刷を開始することもできます.jdk 1.4ではjavax.print.ServiceUIのprintDialogで印刷ダイアログボックスを表示し、printメソッドを呼び出して印刷を開始できます.
2.3プリンタダイアログ
2.3.1 Printableの印刷ダイアログ
印刷を開始する前に、PrinterJob.printDialogで印刷ダイアログボックスを表示できます.これにより、印刷すべきページ番号の範囲を選択し、印刷設定を変更することができます.ローカルダイアログボックスです.
実際、Printableオブジェクトから印刷作業を行う場合、印刷オブジェクトは何ページ印刷する必要があるか分かりません.printメソッドを呼び出し続けるだけです.printメソッドがPrintable.PAGEを返す限りEXISTS値では、printメソッドがPrintable.NO_に戻るまで印刷ジョブが絶えず印刷ページを生成します.SUCH_PAGEの場合、印刷作業が停止します.
印刷作業は、印刷が完了した後にのみ正確なページ数計算が行われるため、ダイアログ上のページ番号範囲はまだ初期化されていない[1999].Java.awt.print.Bookオブジェクトを構築して印刷オブジェクトに渡すことができます.また、指定したフォーマットで印刷するページ数を計算し、印刷対象に渡して、印刷するページ数を正確に知ることもできます.
2.3.2サービスUIの印刷ダイアログ
Printableのダイアログボックスとは異なり、jdk 1.4でServiceUIを提供するプリンタダイアログボックスのデフォルト動作は、新しいAPIで変更されました.デフォルトではダイアログボックスは表示されません.サービスUIクラス呼び出しprintDialogメソッドを使用して、次のような印刷ダイアログボックスを作成する必要があります.
3 Java印刷プログラム設計例
3.1印刷テキスト
3.1.1シーンの適用
フォームのテキスト編集ドメイン(数行、複数ページ)の内容を印刷し、1ページあたり最大54行を印刷する必要があるとしたら、どのように実現しますか?
3.1.2解決方法
基本的な考え方は、まずPrintableインタフェースを実現し、1ページあたり最大54行のフォーマットで合計何ページ印刷する必要があるかを計算し、印刷テキストのボタンがクリックされると、対応する印刷動作を実行する必要があります.印刷テキストの具体的な動作は、Graphics 2 DのdrawString法によって実現することができる.
1、Printableインタフェースの実現

/**//*Graphic         ;PageFormat       (           ,1  1   1/72,1   25.4  。A4    595×842 );page    */

public int print(Graphics g, PageFormat pf, int page) throws PrinterException ...{
  Graphics2D g2 = (Graphics2D)g;
  g2.setPaint(Color.black); //         
  if (page >= PAGES) //                ,      
    return Printable.NO_SUCH_PAGE;
  g2.translate(pf.getImageableX(), pf.getImageableY());//    ,      
  drawCurrentPageText(g2, pf, page); //       
  return Printable.PAGE_EXISTS; //      ,      
}
/**/

/*             */
private void drawCurrentPageText(Graphics2D g2, PageFormat pf, int page) ...{
  String s = getDrawText(printStr)[page]; //             
  //            
  FontRenderContext context = g2.getFontRenderContext();
  Font f = area.getFont();
  String drawText;
  float ascent = 16; //      
  int k, i = f.getSize(), lines = 0;
  while(s.length() > 0 && lines < 54) //     54   
  ...{
    k = s.indexOf( ); //           
    if (k != -1)  //     
    ...{
      lines += 1; //    
      drawText = s.substring(0, k); //       
      g2.drawString(drawText, 0, ascent); //         ,      
      if (s.substring(k + 1).length() > 0) ...{
          s = s.substring(k + 1); //         
        ascent += i;
      }
    }
    else //      
    ...{
      lines += 1; //    
      drawText = s; //       
      g2.drawString(drawText, 0, ascent); //         ,      
      s = ""; //     
    }
  }
}

/**//*                 */
public String[] getDrawText(String s) ...{
  String[] drawText = new String[PAGES]; //         
  for (int i = 0; i < PAGES; i++)
    drawText[i] = ""; //            
  int k, suffix = 0, lines = 0;
  while (s.length() > 0) ...{
    if (lines < 54) //     
    ...{
      k = s.indexOf( );
      if (k != -1) //     
      ...{
        lines += 1; //    
    //           ,            
        drawText[suffix] = drawText[suffix] + s.substring(0, k + 1);
        if (s.substring(k + 1).length() > 0)
          s = s.substring(k + 1);
      }
      else
      ...{
        lines += 1; //    
    //               
        drawText[suffix] = drawText[suffix] + s;
        s = "";
      }
    }
    else //     
    ...{
      lines = 0; //      
      suffix++; //     1
    }
  }
  return drawText;
}

2、印刷する総ページ数を計算する
public int getPagesCount(String curStr)    ...{
  int page = 0;
  int position, count = 0;
  String str = curStr;
  while(str.length() > 0) //        
  ...{
    position = str.indexOf( ); //        
    count += 1; //    
    if (position != -1)
      str = str.substring(position + 1); //         
    else
      str = ""; //       
  }
  if (count > 0)
    page = count / 54 + 1; //      54     
  return page; //         
}

3.1、jdk 1.4以前のバージョンで印刷動作ボタンの傍受を実現し、具体的な印刷操作を完成する

private void printTextAction() ...{
  printStr = area.getText().trim(); //           
  if (printStr != null && printStr.length() > 0) //         
  ...{
    PAGES = getPagesCount(printStr); //       
    PrinterJob myPrtJob = PrinterJob.getPrinterJob(); //        
    PageFormat pageFormat = myPrtJob.defaultPage(); //          
    myPrtJob.setPrintable(this, pageFormat); //      
    if (myPrtJob.printDialog()) //       
    ...{
      try ...{
        myPrtJob.print(); //            
      }
      catch(PrinterException pe) ...{
        pe.printStackTrace();
      }
    }
  }
  else ...{ //         ,         
    JOptionPane.showConfirmDialog(null, "Sorry, Printer Job is Empty, Print Cancelled!", "Empty", JOptionPane.DEFAULT_OPTION,                                   JOptionPane.WARNING_MESSAGE);
  }
}
 

3.2、jdk 1.4の新バージョンで提供されたAPIで印刷動作ボタンの傍受を実現し、具体的な印刷操作を完成する

private void printText2Action() ...{
  printFlag = 0; //      
  printStr = area.getText().trim();//           
  if (printStr != null && printStr.length() > 0) //         
  ...{
    PAGES = getPagesCount(printStr); //       
    //        
    DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
    //         
    PrintService printService = PrintServiceLookup.lookupDefaultPrintService();
    //      
    DocPrintJob job = printService.createPrintJob();
    //      
    PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
    DocAttributeSet das = new HashDocAttributeSet();
    //      
    Doc doc = new SimpleDoc(this, flavor, das);
    //        ,        
    try ...{
      job.print(doc, pras); //            
    }
    catch(PrintException pe) ...{
      pe.printStackTrace();
    }
  }
  else ...{ //         ,         
    JOptionPane.showConfirmDialog(null, "Sorry, Printer Job is Empty, Print Cancelled!", "Empty", JOptionPane.DEFAULT_OPTION,                                   JOptionPane.WARNING_MESSAGE);
  }
}

3.2印刷プレビュー
3.2.1シーンの適用
多くのビジネスアプリケーションでは、画面にページを表示できる印刷プレビューメカニズムが必要です.これにより、印刷結果が気に入らないため、紙を無駄にすることはありません.前のセクションで述べたテキストを印刷する前に、印刷プレビューを行う必要があるとします.では、どうやって実現すればいいのでしょうか.
インタフェース実装図は以下の通りである:(Nextは次のページをプレビューし、Previewは前のページをプレビューし、Closeはプレビューを閉じる)
3.2.2解決方法
基本的な考え方:Java 2プラットフォームの印刷APIは標準的な印刷プレビューダイアログボックスを提供していないが、自分で設計するのも複雑ではない.通常、printメソッドはページ環境をプリンタグラフィック環境に描画し、印刷を実現します.実際、printメソッドでは印刷ページを実際に生成することはできません.印刷対象のコンテンツをグラフィック環境に描画するだけです.したがって、スクリーングラフィック環境を無視し、適切なスケールで印刷ページ全体をスクリーン矩形に収容し、正確な印刷プレビューを実現することができます.
印刷プレビューの設計実装では、主に2つの問題を解決する必要があります.
第一に、印刷内容を適切な割合でスクリーンに描画する方法.
第二に、前後のページをめくる方法です.
次に、この2つの問題の具体的な実装方法を示します.完全な実装は、添付ファイルのPrintPreviewDialog.javaファイルを参照してください.

/**//*              */
public void paintComponent(Graphics g) ...{
  super.paintComponent(g);
  Graphics2D g2 = (Graphics2D)g;
  PageFormat pf = PrinterJob.getPrinterJob().defaultPage(); //      
  double xoff; //               
  double yoff; //               
  double scale; //           
  double px = pf.getWidth(); //    
  double py = pf.getHeight(); //    
  double sx = getWidth() - 1;
  double sy = getHeight() - 1;
  if (px/py < sx/sy) ...{
    scale = sy / py; //    
    xoff = 0.5 * (sx - scale * px); //     
    yoff = 0;
  }
  else ...{
    scale = sx / px; //    
    xoff = 0;
    yoff = 0.5 * (sy - scale * py); //     
  }
  g2.translate((float)xoff, (float)yoff); //    
  g2.scale((float)scale, (float)scale);
  Rectangle2D page = new Rectangle2D.Double(0, 0, px, py); //      
  g2.setPaint(Color.white); //         
  g2.fill(page);
  g2.setPaint(Color.black);//         
  g2.draw(page);
  try ...{
    preview.print(g2, pf, currentPage); //         
  }
  catch(PrinterException pe) ...{
    g2.draw(new Line2D.Double(0, 0, px, py));
    g2.draw(new Line2D.Double(0, px, 0, py));
  }
}

/**//*       */
public void viewPage(int pos) ...{
  int newPage = currentPage + pos;  //           
  if (0 <= newPage && newPage < preview.getPagesCount(printStr)) ...{
    currentPage = newPage; //           
    repaint();
  }
}

このように、「Next」ボタンを押すと、canvas.viewPage(1)を呼び出すだけです.「Preview」ボタンを押すと、canvas.viewPage(-1)を呼び出すだけでプレビューの前後ページをめくることができます.
3.3グラフィックの印刷
3.3.1シーンの適用
実際の応用では、グラフィックを印刷する必要があります.例えば、Java Appletの完全なインタフェースやアプリケーションフォーム、それに含まれるすべてのコンポーネントを印刷する必要がある場合がありますが、どのように実現すればいいのでしょうか.
3.3.2解決方法
基本的な考え方は、JavaのComponentクラスとその派生クラスにprintとprintAllメソッドが提供されており、プロパティを設定すればこの2つのメソッドを直接呼び出すことができ、コンポーネントとグラフィックの印刷を実現することができます.

/**//*              */
private void printFrameAction() ...{
  Toolkit kit = Toolkit.getDefaultToolkit(); //     
  Properties props = new Properties();
  props.put("awt.print.printer", "durango"); //      
  props.put("awt.print.numCopies", "2");
  if (kit != null) ...{
    //            
    PrintJob printJob = kit.getPrintJob(this, "Print Frame", props);
    if (printJob != null) ...{
      Graphics pg = printJob.getGraphics(); //           
      if (pg != null) ...{
        try ...{
          this.printAll(pg); //            
        }
        finally ...{
          pg.dispose(); //      
        }
      }
      printJob.end(); //      
    }
  }
}

3.4印刷ファイル
3.4.1シーンの適用
多くの実際のアプリケーションでは、ユーザーが指定したファイルを印刷する必要がある場合があります.このファイルは、GIF、JPEGなどのグラフィックファイルである可能性があります.TXT、Javaファイルなどのテキストファイルかもしれません.複雑なPDF、DOCファイルなどかもしれません.では、このような印刷ニーズに対して、私たちはどのように実現すればいいのでしょうか.
3.4.2解決方法
基本的な考え方:jdk 1.4以前のバージョンでは、このような印刷機能を実現するには、非常に面倒で複雑であり、想像しにくい.しかし、幸いなことに、jdk 1.4の印刷サービスAPIは、印刷ファイルストリームのクラスと方法のセットを提供しています.これらを利用すると、さまざまなタイプのファイルの印刷機能を非常に便利かつ迅速に実現できます.以下に、一般的な処理方法を示す.

/**//*       */
private void printFileAction() ...{
  //         ,       
  JFileChooser fileChooser = new JFileChooser(SystemProperties.USER_DIR);
  int state = fileChooser.showOpenDialog(this); //         
  if (state == fileChooser.APPROVE_OPTION) //         
  ...{
    File file = fileChooser.getSelectedFile(); //       
    //         
    PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
    //      ,         ,    AUTOSENSE
    DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
    //           
    PrintService printService[] = PrintServiceLookup.lookupPrintServices(flavor, pras);
    //         
    PrintService defaultService = PrintServiceLookup.lookupDefaultPrintService();
    //       
    PrintService service = ServiceUI.printDialog(null, 200, 200, printService, defaultService, flavor, pras);
    if (service != null) ...{
      try ...{
          DocPrintJob job = service.createPrintJob(); //      
        FileInputStream fis = new FileInputStream(file); //         
        DocAttributeSet das = new HashDocAttributeSet();
        Doc doc = new SimpleDoc(fis, flavor, das); //        
        job.print(doc, pras); //       
      }
      catch(Exception e) ...{
          e.printStackTrace();
      }
    }
  }
}

上記の例では、ファイルの種類がまだ特定されていないため、指定されたファイルの印刷フォーマットはDocFlavor.INPUT_として定義されているSTREAM.AUTOSENSE.実際、印刷を行う前に、GIFのようなファイルのフォーマットが確定的に知られている場合は、DocFlavor.INPUT_と定義する必要があります.STREAM.GIF ;PDFの場合はDocFlavor.INPUT_と定義する必要があります.STREAM.PDF;純粋なASCIIファイルであれば、DocFlavor.INPUT_と定義できます.STREAM.TEXT_HTML_US_ASCII.などなど.
jdk 1.4のjavax.print.DocFlavorは、特定のアプリケーションのニーズに応じて適切な選択を行うことができる非常に豊富なファイルストリームタイプを提供しています.具体的なAPI参照ドキュメントは、本明細書の参照資料3を参照することができる.
4終わりの言葉
以上は私が2年余りJ 2 EEの応用開発の中で、Javaで印刷プログラムの設計を行ういくつかの経験を総括して、みんなにいくつか啓示と役に立つことを望みます.現在Javaで印刷機能を実現しているが、MicrosoftのMFC APIに比べて確かに多くの面倒がある.しかしjdk 1.4の発売は、Java以前の弱い印刷機能にとって素晴らしい補完である.前述した印刷プログラムの設計例をよく理解し、応用と拡張すれば、現在のほとんどのアプリケーションの実際のプログラミング問題を解決できると信じています.Javaの更なる発展と完備に従って、きっともっと良くその基礎クラスライブラリと印刷APIを充実させて、Javaで高級な印刷機能を実現することもますます私达のこれらのJavaファンの頭痛の問題にならないと信じています.
記事の出典:http://www.diybl.com/course/3_program/java/javashl/200855/112950.html[/size]