返回介绍

13.17.2 打印

发布于 2024-10-15 23:56:29 字数 9159 浏览 0 评论 0 收藏 0

非常不幸,打印时没有多少事情是可以自动进行的。相反,为完成打印,我们必须经历大量机械的、非 OO(面向对象)的步骤。但打印一个图形化的组件时,可能多少有点儿自动化的意思:默认情况下,print() 方法会调用 paint() 来完成自己的工作。大多数时候这都已经足够了,但假如还想做一些特别的事情,就必须知道页面的几何尺寸。

下面这个例子同时演示了文字和图形的打印,以及打印图形时可以采取的不同方法。此外,它也对打印支持进行了测试:

//: PrintDemo.java
// Printing with Java 1.1
import java.awt.*;
import java.awt.event.*;

public class PrintDemo extends Frame {
  Button 
    printText = new Button("Print Text"),
    printGraphics = new Button("Print Graphics");
  TextField ringNum = new TextField(3);
  Choice faces = new Choice();
  Graphics g = null;
  Plot plot = new Plot3(); // Try different plots
  Toolkit tk = Toolkit.getDefaultToolkit();
  public PrintDemo() {
    ringNum.setText("3");
    ringNum.addTextListener(new RingL());
    Panel p = new Panel();
    p.setLayout(new FlowLayout());
    printText.addActionListener(new TBL());
    p.add(printText);
    p.add(new Label("Font:"));
    p.add(faces);
    printGraphics.addActionListener(new GBL());
    p.add(printGraphics);
    p.add(new Label("Rings:"));
    p.add(ringNum);
    setLayout(new BorderLayout());
    add(p, BorderLayout.NORTH);
    add(plot, BorderLayout.CENTER);
    String[] fontList = tk.getFontList();
    for(int i = 0; i < fontList.length; i++)
      faces.add(fontList[i]);
    faces.select("Serif");
  }
  class PrintData {
    public PrintJob pj;
    public int pageWidth, pageHeight;
    PrintData(String jobName) {
      pj = getToolkit().getPrintJob(
        PrintDemo.this, jobName, null);
      if(pj != null) {
        pageWidth = pj.getPageDimension().width;
        pageHeight= pj.getPageDimension().height;
        g = pj.getGraphics();
      }
    }
    void end() { pj.end(); }
  }
  class ChangeFont {
    private int stringHeight;
    ChangeFont(String face, int style,int point){
      if(g != null) {
        g.setFont(new Font(face, style, point));
        stringHeight = 
          g.getFontMetrics().getHeight();
      }
    }
    int stringWidth(String s) {
      return g.getFontMetrics().stringWidth(s);
    }
    int stringHeight() { return stringHeight; }
  }
  class TBL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      PrintData pd = 
        new PrintData("Print Text Test");
      // Null means print job canceled:
      if(pd == null) return;
      String s = "PrintDemo";
      ChangeFont cf = new ChangeFont(
        faces.getSelectedItem(), Font.ITALIC,72);
      g.drawString(s, 
        (pd.pageWidth - cf.stringWidth(s)) / 2,
        (pd.pageHeight - cf.stringHeight()) / 3);

      s = "A smaller point size";
      cf = new ChangeFont(
        faces.getSelectedItem(), Font.BOLD, 48);
      g.drawString(s, 
        (pd.pageWidth - cf.stringWidth(s)) / 2,
        (int)((pd.pageHeight - 
           cf.stringHeight())/1.5));
      g.dispose();
      pd.end();
    }
  }
  class GBL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      PrintData pd = 
        new PrintData("Print Graphics Test");
      if(pd == null) return;
      plot.print(g);
      g.dispose();
      pd.end();
    }
  }
  class RingL implements TextListener {
    public void textValueChanged(TextEvent e) {
      int i = 1;
      try {
        i = Integer.parseInt(ringNum.getText());
      } catch(NumberFormatException ex) {
        i = 1;
      }
      plot.rings = i;
      plot.repaint();
    }
  }
  public static void main(String[] args) {
    Frame pdemo = new PrintDemo();
    pdemo.setTitle("Print Demo");
    pdemo.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    pdemo.setSize(500, 500);
    pdemo.setVisible(true);
  }
}

class Plot extends Canvas {
  public int rings = 3;
}

class Plot1 extends Plot {
  // Default print() calls paint():
  public void paint(Graphics g) {
    int w = getSize().width;
    int h = getSize().height;
    int xc = w / 2;
    int yc = w / 2;
    int x = 0, y = 0;
    for(int i = 0; i < rings; i++) {
      if(x < xc && y < yc) {
        g.drawOval(x, y, w, h);
        x += 10; y += 10;
        w -= 20; h -= 20;
      }
    }
  }
} 

class Plot2 extends Plot {
  // To fit the picture to the page, you must
  // know whether you're printing or painting:
  public void paint(Graphics g) {
    int w, h;
    if(g instanceof PrintGraphics) {
      PrintJob pj = 
        ((PrintGraphics)g).getPrintJob();
      w = pj.getPageDimension().width;
      h = pj.getPageDimension().height;
    } 
    else {
      w = getSize().width;
      h = getSize().height;
    }
    int xc = w / 2;
    int yc = w / 2;
    int x = 0, y = 0;
    for(int i = 0; i < rings; i++) {
      if(x < xc && y < yc) {
        g.drawOval(x, y, w, h);
        x += 10; y += 10;
        w -= 20; h -= 20;
      }
    }
  }
} 

class Plot3 extends Plot {
  // Somewhat better. Separate 
  // printing from painting:
  public void print(Graphics g) {
    // Assume it's a PrintGraphics object:
    PrintJob pj = 
      ((PrintGraphics)g).getPrintJob();
    int w = pj.getPageDimension().width;
    int h = pj.getPageDimension().height;
    doGraphics(g, w, h);
  }
  public void paint(Graphics g) {
    int w = getSize().width;
    int h = getSize().height;
    doGraphics(g, w, h);
  }
  private void doGraphics(
      Graphics g, int w, int h) {
    int xc = w / 2;
    int yc = w / 2;
    int x = 0, y = 0;
    for(int i = 0; i < rings; i++) {
      if(x < xc && y < yc) {
        g.drawOval(x, y, w, h);
        x += 10; y += 10;
        w -= 20; h -= 20;
      }
    }
  }
} ///:~

这个程序允许我们从一个选择列表框中选择字体(并且我们会注意到很多有用的字体在 Java 1.1 版中一直受到严格的限制,我们没有任何可以利用的优秀字体安装在我们的机器上)。它使用这些字体去打出粗体,斜体和不同大小的文字。另外,一个新型组件调用过的绘图被创建,以用来示范图形。当打印图形时,绘图拥有的 ring 将显示在屏幕上和打印在纸上,并且这三个衍生类 Plot1,Plot2,Plot3 用不同的方法去完成任务以便我们可以看到我们选择的事物。同样,我们也能在一个绘图中改变一些 ring——这很有趣,因为它证明了 Java 1.1 版中打印的脆弱。在我的系统里,当 ring 计数显示“too high”(究竟这是什么意思?)时,打印机给出错误信息并且不能正确地工作,而当计数给出“low enough”信息时,打印机又能工作得很好。我们也会注意到,当打印到看起来实际大小不相符的纸时页面的大小便产生了。这些特点可能被装入到将来发行的 Java 中,我们可以使用这个程序来测试它。

这个程序为促进重复使用,不论何时都可以封装功能到内部类中。例如,不论何时我想开始打印工作(不论图形或文字),我必须创建一个 PrintJob 打印工作对象,该对象拥有它自己的连同页面宽度和高度的图形对象。创建的 PrintJob 打印工作对象和提取的页面尺寸一起被封装进 PrintData class 打印类中。

1. 打印文字

打印文字的概念简单明了:我们选择一种字体和大小,决定字符串在页面上存在的位置,并且使用 Graphics.drawSrting() 方法在页面上画出字符串就行了。这意味着,不管怎样我们必须精确地计算每行字符串在页面上存在的位置并确定字符串不会超出页面底部或者同其它行冲突。如果我们想进行字处理,我们将进行的工作与我们很相配。ChangeFont 封装进少量从一种字体到其它的字体的变更方法并自动地创建一个新字体对象和我们想要的字体,款式(粗体和斜体——目前还不支持下划线、空心等)以及点阵大小。它同样会简单地计算字符串的宽度和高度。当我们按下“Print text”按钮时,TBL 接收器被激活。我们可以注意到它通过反复创建 ChangeFont 对象和调用 drawString() 来在计算出的位置打印出字符串。注意是否这些计算产生预期的结果。(我使用的版本没有出错。)

2. 打印图形

按下“Print graphics”按钮时,GBL 接收器会被激活。我们需要打印时,创建的 PrintData 对象初始化,然后我们简单地为这个组件调用 print() 打印方法。为强制打印,我们必须为图形对象调用 dispose() 处理方法,并且为 PrintData 对象调用 end() 结束方法(或改变为为 PrintJob 调用 end() 结束方法。)

这种工作在绘图对象中继续。我们可以看到基础类绘图是很简单的——它扩展画布并且包括一个中断调用 ring 来指明多少个集中的 ring 需要画在这个特殊的画布上。这三个衍生类展示了可达到一个目的的不同的方法:画在屏幕上和打印的页面上。

Plot1 采用最简单的编程方法:忽略绘画和打印的不同,并且过载 paint() 绘画方法。使用这种工作方法的原因是默认的 print() 打印方法简单地改变工作方法转而调用 Paint()。但是,我们会注意到输出的尺寸依赖于屏幕上画布的大小,因为宽度和高度都是在调用 Canvas.getSize() 方法时决定是,所以这是合理的。如果我们图像的尺寸一值都是固定不变的,其它的情况都可接受。当画出的外观的大小如此的重要时,我们必须深入了解的尺寸大小的重要性。不凑巧的是,就像我们将在 Plot2 中看到的一样,这种方法变得很棘手。因为一些我们不知道的好的理由,我们不能简单地要求图形对象以它自己的大小画出外观。这将使整个的处理工作变得十分的优良。相反,如果我们打印而不是绘画,我们必须利用 RTTI instanceof 关键字(在本书 11 章中有相应描述)来测试 PrintGrapics,然后下溯造型并调用这独特的 PrintGraphics 方法:getPrintJob() 方法。现在我们拥有 PrintJob 的句柄并且我们可以发现纸张的高度和宽度。这是一种 hacky 的方法,但也许这对它来说是合理的理由。(在其它方面,到如今我们看到一些其它的库设计,因此,我们可能会得到设计者们的想法。)

我们可以注意到 Plot2 中的 paint() 绘画方法对打印和绘图的可能性进行审查。但是因为当打印时 Print() 方法将被调用,那么为什么不使用那种方法呢?这种方法同样也在 Plot3 中也被使用,并且它消除了对 instanceof 使用的需求,因为在 Print() 方法中我们可以假设我们能对一个 PrintGraphics 对象造型。这样也不坏。这种情况被放置公共绘画代码到一个分离的 doGraphics() 方法的办法所改进。

2. 在程序片内运行帧

如果我们想在一个程序片中打印会怎以样呢?很好,为了打印任何事物我们必须通过工具组件对象的 getPrintJob() 方法拥有一个 PrintJob 对象,设置唯一的一个帧对象而不是一个程序片对象。于是它似乎可能从一个应用程序中打印,而不是从一个程序片中打印。但是,它变为我们可以从一个程序片中创建一个帧(相反的到目前为止,我在程序片或应用程序例子中所做的,都可以生成程序片并安放帧。)。这是一个很有用的技术,因为它允许我们在程序片中使用一些应用程序(只要它们不妨碍程序片的安全)。但是,当应用程序窗口在程序片中出现时,我们会注意到 WEB 浏览器插入一些警告在它上面,其中一些产生“Warning:Applet Window.(警告:程序片窗口)”的字样。

我们会看到这种技术十分直接的安放一个帧到程序片中。唯一的事是当用户关闭它时我们必须增加帧的代码(代替调用 System.exit()):

//: PrintDemoApplet.java
// Creating a Frame from within an Applet
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class PrintDemoApplet extends Applet {
  public void init() {
    Button b = new Button("Run PrintDemo");
    b.addActionListener(new PDL());
    add(b);
  }
  class PDL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      final PrintDemo pd = new PrintDemo();
      pd.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e){
          pd.dispose();
        }
      });
      pd.setSize(500, 500);
      pd.show();
    }
  }
} ///:~

伴随 Java 1.1 版的打印支持功能而来的是一些混乱。一些宣传似乎声明我们能在一个程序片中打印。但 Java 的安全系统包含了一个特点,可停止一个正在初始化打印工作的程序片,初始化程序片需要通过一个 Web 浏览器或程序片浏览器来进行。在写作这本书时,这看起来像留下了一个未定的争议。当我在 WEB 浏览器中运行这个程序时,printdemo(打印样本)窗口正好出现,但它却根本不能从浏览器中打印。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文