Impression

Chapitres traités   

Durant cette étude, nous allons voir comment imprimer facilement une image (qui peut être en réalité que du texte) sur une seule feuille de papier, comment gérer une impression sur plusieurs pages et comment tirer profit de l'élégance du modèle graphique Java2D pour générer facilement une boîte de dialogue affichant un aperçu de l'impression.

La plate-forme Java permet également d'imprimer des composants de l'interface utilisateur. Il suffit généralement de faire appel à la méthode print() du composant à imprimer.

Afin de bien maîtriser l'ensemble des informations contenues dans cette étude, reportez-vous à l'étude qui porte sur le Graphisme 2D.
§

Choix du chapitre Imprimer des images

Dans ce chapitre, nous allons aborder ce qui est probablement la situation d'impression la plus courante : une image 2D. Bien entendu, l'image peut contenir du texte de différentes polices, voire n'être constituée que de texte. Pour créer une impression, il convient de prendre en compte au moins deux éléments :

  1. Vous devez fournir un objet qui implémente l'interface java.awt.print.Printable. Cette interface possède une seule méthode : print().
  2. Vous devez créer un travail d'impression.
Interface Printable
int print(Graphics surface, PageFormat page, int numéro)
Cette méthode est appelée chaque fois que le gestionnaire d'impression a besoin d'une page formatée pour l'impression. Votre code dessine le texte et l'image qui doivent être imprimés dans le contexte graphique. Le format de la page indique les dimensions du papier et les marges d'impression. Le numéro de page indique quelle page vous devez afficher.

Travail d'impression - PrinterJob

Pour créer un travail d'impression, utilisez la classe java.awt.print.PrinterJob :

  1. Tout d'abord, appelez la méthode statique getPrinterJob() pour obtenir un objet de travail d'impression.
  2. Utilisez ensuite la méthode setPrintable() pour obtenir l'objet qui imlémente l'interface Printable qui doit afficher le contenu de la page.

    Printable canevas = ... ;
    PrinterJob travail = PrinterJob.getPrinterJob();
    travail.setPrintable(canevas);

Attention : Il existe une classe PrintJob qui gère les impressions avec le style du JDK1.1. Cette classe est maintenant obsolète. Ne la confondez pas avec la classe PrinterJob.

Avant de commencer un nouveau travail d'impression, n'oubliez pas d'appeler la méthode printDialog() pour afficher une boîte de dialogue d'impression. Cette boîte de dialogue permet à l'utilisateur de sélectionner l'imprimante, la plage des pages à imprimer et divers paramètres d'impression.

Collecter les paramètres d'impression - PrintRequestAttributSet - et lancer l'impression

Il est possible de collecter les paramètres d'impression dans un objet d'une classe qui implémente l'interface javax.print.attribute.PrintRequestAttributSet dans la méthode printDialog(). Java fournit une classe javax.print.attribute.HashPrintRequestAttributSet dans ce but.

...
HashPrintRequestAttributSet
attributs = new HashPrintRequestAttributSet();
travail.printDialog(attributs);

La méthode printDialog() renvoie true si l'utilisateur a cliqué sur OK et false s'il a annulé la boîte de dialogue. Si l'utilisateur a accepté, appelez la méthode print() de la classe PrinterJob pour lancer la procédure d'impression. La méthode print() peut, si un problème survient, déclencher une exception PrinterException.
if (travail.printDialog(attributs)) {
  try {
    travail.print();
  }
  catch (PrinterException exception) { ... }

Au cours de l'impression, la méthode print() de la classe PrinterJob effectue des appels répétés à la méthode print() de l'objet implémentant l'interface Printable associé à la tâche.

Etant donné que le travail d'impression ne sait pas combien de pages vous souhaitez imprimer, il se contente d'appeler plusieurs fois la méthode print(). Tant que la méthode print() renvoie la valeur Printable.PAGE_EXISTS, le travail d'impression continue de produire des pages. Lorsque la méthode print() renvoie Printable.NO_SUCH_PAGE, le travail d'impression s'arrête.

Attention ! Le nombre de pages que le travail d'impression passe à la méthode print() commence par 0.
§

Par conséquent, le travail d'impression ne sait pas précisément combien de pages il faut imprimer, avant que l'impression totale ne soit terminée. C'est pour cette raison que la boîte de dialogue d'impression affiche une plage d'impression correspondant aux pages 1 à 1. Vous verrez dans le prochain chapitre comment éviter cet inconvénient en fournissant un objet java.awt.print.Book au travail d'impression.

Processus d'impression (avancement par bandes)

Pendant le processus d'impression, le travail d'impression appelle plusieurs fois la méthode print() de l'objet Printable. Ce travail d'impression a l'autorisation d'effectuer plusieurs appels pour la même page. Par conséquent, il ne faut pas compter les pages dans la méthode print(), mais plutôt se fonder sur le paramètre de nombre de pages.

Il existe une bonne raison pour que le travail d'impression puisse appeler plusieurs fois la méthode print() pour la même page. Certaines imprimantes, en particulier les imprimantes matricielles et à jet d'encre, utilisent un effet de bandes. Elles impriment une bande à la fois, avancent la feuille de papier, puis impriment la bande suivante. Cela fournit au travail d'impression une technique pour gérer la taille du fichier d'impression.

Si le travail d'impression demande à l'objet Printable d'imprimer une bande, il définit une zone de clipping dans le contexte graphique correspondant à la bande sélectionnée, puis appelle la méthode print(). Ses opérations d'affichage seront alors restreintes au rectangle de la bande, et seuls les éléments graphiques qui se trouvent dans cet bande seront affichés.

Votre méthode print() n'a pas forcément besoin d'être au courant de ce procédé, à une seule exception près : elle ne doit pas interférer avec le rectangle de clipping.

Attention : L'objet Graphics que votre méthode print() récupère est également restreint à la zone imprimable, c'est-à-dire que les marges y sont imprimées. Si vous remplacez la zone de clipping, il se peut que vous imprimiez en dehors des marges. En particulier dans un contexte graphique d'impression, vous devez respecter la zone de clipping. Appelez donc clip() et non setClip(), pour réduire la zone de clipping. Si vous êtes amenés à réduire la zone de clipping, vérifiez que vous appelez bien getClip() au début de votre méthode print() et que vous restaurez cette zone de clipping.

Pour en savoir plus sur le cliping, revoir les cours sur le Graphisme 2D.
§

Le paramètre PageFormat de la méthode print()

Le paramètre PageFormat de la méthode print() contient des informations sur la page imprimée. Ainsi, les méthodes getWidth() et getHeight() renvoient la taille du papier, mesurée en points.

Un point correspond à 1/72ème de pouce, un pouce valant 25,4 millimètres. Par exemple une feuille de papier A4 contient 595 par 842 points. La bibliothèque d'impression se sert de points pour deux raisons.
  1. Les tailles de papier et les marges sont mesurées en points.
  2. L'unité par défaut pour tous les contextes graphiques d'impression est aussi le point.

Les méthodes getWidth() et getHeight() de la classe PageFormat fournissent la taille complète du papier. Vous ne pouvez pas imprimer à n'importe quel endroit d'une page. La plupart du temps, les utilisateurs sélectionnent des marges, et même s'ils ne le font pas, les imprimantes ont besoin d'un faible espace pour entraîner la feuille sur laquelle elles impriment ; cet espace n'est par conséquent pas imprimable.

Les méthodes getImageableWidth() et getImageableHeight() fournissent les dimensions de la zone réellement imprimable. Cependant les marges doivent être symétriques : il vous faut également connaître le coin supérieur gauche de la zone graphique, qui peut être obtenu grâce aux méthodes suivantes : getImageableX() et getImageableY().

Astuce : le contexte graphique que vous recevez dans la méthode print() est clippé pour exclure les marges. Mais l'origine du système de coordonnées reste néanmoins le coin supérieur gauche de la feuille de papier. Il est donc intéressant d'effectuer une translation sur le système de coordonnées pour commencer au coin supérieur gauche de la zone graphique. Commencez simplement votre méthode print() par :
int print(Graphics surface, PageFormat page, int numéro) { 
   surface.translate(page.getImageableX(), page.getImageableY());
   ...
}
Si vous souhaitez que vos utilisateurs puissent choisir les paramètres des marges ou qu'ils puissent changer d'orientation (portrait ou paysage) sans définir d'autres attributs d'impression, vous pouvez appeler la méthode pageDialog() de la classe PrinterJob :
PageFormat page = travail.pageDialog(attributs);

L'un des onglets de la boîte de dialogue d'impression contient la boîte de dialogue de configuration de la page. Vous souhaiterez peut-être donner aux utilisateurs le choix de définir un format de page avant l'impression, en particulier si votre programme présente un affichage en WYSIWYG des pages à imprimer. La méthode pageDialog() renvoie un objet PageFormat contenant les réglages de l'utilisateur.


Utiliser des boîtes de dialogue d'impression natives

Avant la version SDK 1.4, le système d'impression utilisait des boîtes de dialogue de configuration d'impression et de mise en page natives de la plate-forme hôte. Pour afficher une boîte de dialogue native, appelez la méthode printDialog() sans aucun paramètre. Il n'existe aucune méthode permettant de rassembler les paramètres utilisateur dans un ensemble d'attributs

L'un des avantages potentiels qui existent à utiliser une boîte de dialogue d'impression native réside dans le fait que certains pilotes d'imprimante possèdent des fonctions spéciales qui ne sont pas accessibles par le biais d'une boîte de dialogue inter-plates-formes.

Pour afficher une boîte de dialogue de mise en page d'impression native, vous passez un objet par défaut de type PageFormat à la méthode pageDialog(). La méthode clone l'objet, le modifie en fonction des choix de l'utilisateur dans la boîte de dialogue, puis renvoie l'objet cloné.
PageFormat défaut = travail.defaultPage();
PageFormat
page = travail.pageDialog(défaut);

Exemple de mise en oeuvre

Je vous propose de mettre en oeuvre une petite application qui permet d'afficher un ensemble de formes sur un écran et sur une page imprimée :

codage correspondant
package impression;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.geom.*;
import java.awt.print.*;
import javax.print.attribute.*;
import javax.swing.*;

public class Imprimante extends JFrame {
   private PanneauImpression canevas = new PanneauImpression();
   private PrintRequestAttributeSet attributs = new HashPrintRequestAttributeSet();
   private JToolBar barre = new JToolBar();
   
   public Imprimante() {
       super("Impression");
       add(canevas);
       add(barre, BorderLayout.NORTH);
       barre.add(new AbstractAction("Imprimer") {
         public void actionPerformed(ActionEvent e) {
            try {
               PrinterJob travail = PrinterJob.getPrinterJob();
               travail.setPrintable(canevas);
               if (travail.printDialog(attributs)) {
                  travail.print(attributs);
               }
            } 
            catch (PrinterException ex) {
               JOptionPane.showMessageDialog(Imprimante.this, ex);
            }
         }
       });
       barre.add(new AbstractAction("Mise en page") {
         public void actionPerformed(ActionEvent e) {
            PrinterJob travail = PrinterJob.getPrinterJob();
            travail.pageDialog(attributs);
         }
      });
       setSize(330, 300);
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       setVisible(true);
   }

   public static void main(String[] args) { new Imprimante(); }
    
   private class PanneauImpression extends JComponent implements Printable {
      @Override
      protected void paintComponent(Graphics g) {
         super.paintComponent(g);
         dessinePage((Graphics2D) g);
      }

      @Override
      public int print(Graphics g, PageFormat page, int numéro) 
      throws PrinterException {
         if (numéro>=1) return Printable.NO_SUCH_PAGE;
         Graphics2D surface = (Graphics2D) g;
         surface.translate(page.getImageableX(), page.getImageableY());
         surface.draw(new Rectangle2D.Double(0, 0, page.getImageableWidth(), page.getImageableHeight()));
         dessinePage(surface);
         return Printable.PAGE_EXISTS;
      }
       
      private void dessinePage(Graphics2D surface) {
         surface.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
         CubicCurve2D pétale = new CubicCurve2D.Double(150, 130, 10, -20, 290, -20, 150, 130);
         surface.setStroke(new BasicStroke(5));
         surface.draw(pétale); 
         surface.setFont(new Font("SansSerif", Font.BOLD+Font.ITALIC, 64));
         surface.drawString("Bonjour !", 10, 190);         
      }
   }
}

 

Choix du chapitre Imprimer plusieurs pages

Dans la pratique, vous ne passerez pas d'objet Printable brut à un travail d'impression. Il vaut mieux obtenir un objet d'une classe qui implémente l'interface java.awt.print.Pageable. La plate-forme Java fournit une classe de ce type, appelée java.awt.print.Book (ou livre). Un livre est composé de plusieurs sections, chacune de ces sections étant un objet Printable.

Vous pouvez ainsi créer un livre en ajoutant des objets Printable et leur nombre de pages :
Book livre = new Book();
PrinterJob travail = PrinterJob.getPrinterJob();
PageFormat page = travail.pageDialog(attributs);
Printable couverture = ... ;
Printable corps = ... ;
livre.append(couverture, page); // ajoute une seule page livre.append(corps, page, nombre); ...
Ensuite la méthode setPageable() sert à passer l'objet Book au travail d'impression :
travail.setPageable(livre);

Le travail d'impression sait maintenant exactement combien de pages il devra imprimer. La boîte de dialogue d'impression affiche alors un nombre de pages correct et l'utilisateur peut sélectionner toutes ces pages ou un de leurs sous-ensembles.

Attention, lorsque le travail d'impression appelle la méthode print() des sections Printable, il passe le nombre de pages courant du livre, et non celui de chaque section, comme étant le nombre de pages courant. C'est assez compliqué, puisque chaque section doit connaître le nombre de pages de toutes les sections précédentes, et dans l'ordre, pour que le paramètre de nombre de pages reste valable.

Du point de vue du programmeur, le plus grand problème soulevé par l'utilisation de la classe Book est qu'il faut connaître le nombre de pages que chaque section comprendra au moment de l'impression. Votre classe Printable a donc besoin d'un algorithme de mise en page qui calcule la mise en page finale des pages imprimées.

Avant que l'impression ne commence, appelez cet algorithme pour calculer les sauts de page et le décomptage des pages. Vous pouvez aussi conserver les informations de mise en page pour qu'elles soient accessibles plus facilement pendant l'impression.

L'utilisateur pouvant avoir modifié le format de la page, il convient de se prémunir contre cette éventualité. Si cela se produit, vous devez recalculer la mise en page, même si les données que vous souhaitez imprimer n'ont pas été modifiées.

Mise en oeuvre

Je vous propose de mettre en oeuvre une application qui recense les photos présentes dans un répertoire et de pouvoir les imprimer chacune sur une feuille différente et dont la largeur de la photo correspond à la zone imprimable de la page.

codage correspondant
package impression;

import java.awt.*;
import java.awt.event.*;
import java.awt.print.*;
import java.io.*;
import java.text.MessageFormat;
import javax.print.attribute.*;
import javax.swing.*;

public class Imprimante extends JFrame {
   private File[] fichiers = new File("C:/Photos/").listFiles();
   private Box panneau = Box.createVerticalBox();
   private JScrollPane ascenceur = new JScrollPane(panneau, 
                                 ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 
                                 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
   private String html = "<html><table>"
                      +"<tr><td>{0}</td></tr>"
                      +"<tr><td>{1} x {2}</td></tr>"
                      +"<tr><td>{3} octets</td></tr>" 
                      +"<tr><td>{4, date, full}</td></tr>" 
                      +"</table></html>"; 
   private JToolBar barre = new JToolBar();
   
   public Imprimante(){
      super("Photos");
      for (File fichier : fichiers) {
         Image image = new ImageIcon(fichier.getPath()).getImage();
         int largeur = image.getWidth(null);
         int hauteur = image.getHeight(null);
         Icon icône = new ImageIcon(image.getScaledInstance(200, -1, Image.SCALE_DEFAULT));         
         String intitulé = MessageFormat.format(html, fichier.getName(), largeur, hauteur, fichier.length(), fichier.lastModified());
         JLabel étiquette = new JLabel(intitulé, icône, JLabel.LEFT);
         étiquette.setOpaque(true);
         étiquette.setBackground(Color.GREEN);
         panneau.add(Box.createVerticalStrut(5));
         panneau.add(étiquette);         
      }
      add(ascenceur);
      add(barre, BorderLayout.NORTH);
      barre.add(new AbstractAction("Imprimer") {
         public void actionPerformed(ActionEvent e) {
            try {
               Book pages = new Book();
               PrintRequestAttributeSet attributs = new HashPrintRequestAttributeSet();
               PrinterJob travail = PrinterJob.getPrinterJob();
               PageFormat page = travail.getPageFormat(attributs);
               for (File fichier : fichiers) pages.append(new Impression(new ImageIcon(fichier.getPath()).getImage()), page);
               travail.setPageable(pages);
               if (travail.printDialog(attributs)) {
                  travail.print(attributs);
               }
            } 
            catch (PrinterException ex) { JOptionPane.showMessageDialog(Imprimante.this, ex); }
         }
       });
      setSize(400, 300);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }
   
   public static void main(String[] args) { new Imprimante(); }
   
   private class Impression implements Printable {
      private Image image;
      
      public Impression(Image image) {
         this.image = image;
      }
      
      @Override
      public int print(Graphics g, PageFormat page, int numéro) 
      throws PrinterException {
         if (numéro>=fichiers.length) return Printable.NO_SUCH_PAGE;
         Graphics2D surface = (Graphics2D) g;
         double ratio = (double)image.getWidth(null) / image.getHeight(null);
         int largeur = (int) page.getImageableWidth();
         int hauteur = (int) (page.getImageableWidth() / ratio);
         surface.drawImage(image, (int)page.getImageableX(), (int)page.getImageableY(), largeur , hauteur, null);
         return Printable.PAGE_EXISTS;
      }
   }      
}

 

Choix du chapitre Réglages du format d'impression par programme

Il est possible d'intervenir sur les réglages propres au papier sur lequel l'impression va se faire. Nous pouvons ainsi choisir le format du papier à utiliser et définir la zone d'impression. La classe java.awt.print.Paper est prévue à cet effet et possède deux méthodes pour spécifier les valeurs que nous venons d'évoquer :

  1. setSize(double largeur, double hauteur) : Définie la largeur et la hauteur de la page (toute la surface du papier). Je rappelle qu'un point correspond à 1/72ème de pouce, un pouce valant 25,4 millimètres. Par exemple une feuille de papier A4 contient 595 par 842 points.
  2. setImageableArea(double x, double y, double largeur, double hauteur) : Définie l'emplacement, la largeur et la hauteur de la zone imprimable.

Cette classe Paper possède en plus toutes les méthodes de lecture que nous connaissons déjà : getWidth(), getHeight(), getImageableHeight(), getImageableWidth(), getImageableX() et getImageableY().

Prise en compte du nouveau papier

Une fois que votre papier est créé et parfaitement configuré, vous devez l'intégrer ensuite au système de page à imprimer au moyen de la méthode setPaper() de la classe PageFormat.

PrintRequestAttributeSet attributs = new HashPrintRequestAttributeSet();
PrinterJob travail = PrinterJob.getPrinterJob();
PageFormat page = travail.getPageFormat(attributs);
Paper papier = new Paper();
papier.setSize(595, 842); // format A4
papier.setImageableArea(10, 10, papier.getWidth()-20, papier.getHeight()-20); // marge de 10 points de par et d'autre de la zone d'impression
page.setPaper(papier);

Choix du mode d'impression

Tant que nous y sommes, nous pouvons aussi choisir l'orientation (mode portrait ou mode paysage) de l'impression sur la page au moyen de la méthode setOrientation() de la classe PageFormat. Vous devez alors préciser l'un des paramètres suivants :

  1. PageFormat.LANDSCAPE : mode paysage.
  2. PageFormat.PORTRAIT : mode portrait.
  3. PageFormat.REVERSE_LANDSCAPE : mode paysage inversé.

page.setOrientation(PageFormat.LANDSCAPE); // mode paysage.

Exemple de mise en oeuvre

A titre d'exemple, je vous propose de reprendre l'application précédente. Cette fois-ci, les photos sont imprimées en mode paysage avec une marge plus petite de 10 points.

codage correspondant

package impression;
...
public class Imprimante extends JFrame {
...
      barre.add(new AbstractAction("Imprimer") {
         public void actionPerformed(ActionEvent e) {
            try {
               Book pages = new Book();
               PrintRequestAttributeSet attributs = new HashPrintRequestAttributeSet();
               PrinterJob travail = PrinterJob.getPrinterJob();
               PageFormat page = travail.getPageFormat(attributs);
               Paper papier = new Paper();
               papier.setSize(595, 842);
               papier.setImageableArea(10, 10, papier.getWidth()-20, papier.getHeight()-20);
               page.setPaper(papier);              
               page.setOrientation(PageFormat.LANDSCAPE);
...
            } 
            catch (PrinterException ex) { JOptionPane.showMessageDialog(Imprimante.this, ex); }
         }
       });
      setSize(400, 300);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }
...
   private class Impression implements Printable {
...
      @Override
      public int print(Graphics g, PageFormat page, int numéro) throws PrinterException {
         if (numéro>=fichiers.length) return Printable.NO_SUCH_PAGE;
         Graphics2D surface = (Graphics2D) g;
         double ratio = (double)image.getWidth(null) / image.getHeight(null);
         int hauteur = (int) page.getImageableHeight();
         int largeur = (int) (page.getImageableHeight() * ratio);         
         surface.drawImage(image, (int)page.getImageableX(), (int)page.getImageableY(), largeur , hauteur, null);
         return Printable.PAGE_EXISTS;
      }
   }      
}

 

Choix du chapitre Aperçu d'impression

La plupart des programmes professionnels possèdent un mécanisme d'aperçu qui vous permet d'examiner vos pages à l'écran avant de les imprimer, pour ne pas gaspiller de papier si les paramètres de l'impression ne sont pas corrects. Les classes d'impression de la plate-forme Java ne fournissent pas de boîtes de dialogue d'aperçu standard. Mais il est assez simple d'en concevoir une.

L'astuce est de concevoir une boîte de dialogue qui prend en compte, soit un objet Printable, soit un Book, avec un objet PageFormat. La boîte doit posséder, par ailleurs, de boutons Suivant et Précédent afin de parcourir l'ensemble des pages à imprimer. Pour finir, la méthode paintComponent() de la boîte de dialogue appelle la méthode print() de l'objet Printable pour la page requise.

Normalement, la méthode print() dessine le contexte de la page sur le contexte graphique d'impression. Cependant, nous devons passer le contexte d'écran graphique, éventuellement après un changement d'échelle, de sorte que la page imprimée tout entière tienne à l'intérieur d'un petit rectangle de l'écran.

La classe Book possède deux méthodes qui peuvent s'avérer utiles pour la mise en oeuvre d'un aperçu : la méthode getNumberOfPages() qui comme son nom l'indique permet de connaître le nombre de pages à imprimer, et la méthode getPrintable(int) qui retourne un objet Printable afin de permettre sa visualisation sur un panneau quelconque. Il faut alors préciser la page à afficher. Pour afficher l'aperçu, il suffit tout simplement de lancer la méthode print() de l'interface Printable en spécifiant juste le contexte graphique que nous désirons prendre en compte, celui donc qui correspond au panneau à afficher.

La méthode print() ne sait jamais quand elle produit réellement des pages imprimées. Elle se contente de dessiner dans le contexte graphique, en produisant par conséquent un affichage microscopique sur l'écran. C'est une démonstration très convaincante de la puissance du modèle graphique de Java 2D.

Exemple de mise en oeuvre

Je vous propose simplement de reprendre l'application précédente à laquelle je vais rajouter un aperçu avant impression.

codage correspondant
package impression;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.print.*;
import java.io.*;
import java.text.MessageFormat;
import javax.print.attribute.*;
import javax.swing.*;

public class Imprimante extends JFrame {
   private File[] fichiers = new File("C:/Photos/").listFiles();
   private Box panneau = Box.createVerticalBox();
   private JScrollPane ascenceur = new JScrollPane(panneau, 
                                 ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 
                                 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
   private String html = "<html><table>"
                      +"<tr><td>{0}</td></tr>"
                      +"<tr><td>{1} x {2}</td></tr>"
                      +"<tr><td>{3} octets</td></tr>" 
                      +"<tr><td>{4, date, full}</td></tr>" 
                      +"</table></html>"; 
   private JToolBar barre = new JToolBar();
   private Book pages = new Book();
   private PrintRequestAttributeSet attributs = new HashPrintRequestAttributeSet();
   private PrinterJob travail = PrinterJob.getPrinterJob();
   private PageFormat page = travail.getPageFormat(attributs);   
   private Aperçu boîte;
   
   public Imprimante(){
      super("Photos");
      for (File fichier : fichiers) {
         Image image = new ImageIcon(fichier.getPath()).getImage();
         int largeur = image.getWidth(null);
         int hauteur = image.getHeight(null);
         Icon icône = new ImageIcon(image.getScaledInstance(200, -1, Image.SCALE_DEFAULT));         
         String intitulé = MessageFormat.format(html, fichier.getName(), largeur, hauteur, fichier.length(), fichier.lastModified());
         JLabel étiquette = new JLabel(intitulé, icône, JLabel.LEFT);
         étiquette.setOpaque(true);
         étiquette.setBackground(Color.GREEN);
         panneau.add(Box.createVerticalStrut(5));
         panneau.add(étiquette);         
         pages.append(new Impression(new ImageIcon(fichier.getPath()).getImage()), page);
      }
      add(ascenceur);
      add(barre, BorderLayout.NORTH);
      barre.add(new AbstractAction("Aperçu avant impression") {
         public void actionPerformed(ActionEvent e) {
            if (boîte==null) boîte = new Aperçu();
            boîte.setVisible(true);
         }
      });
      barre.add(new AbstractAction("Imprimer") {
         public void actionPerformed(ActionEvent e) {
            try {
               travail.setPageable(pages);
               if (travail.printDialog(attributs)) {
                  travail.print(attributs);
               }
            } 
            catch (PrinterException ex) { JOptionPane.showMessageDialog(Imprimante.this, ex); }
         }
       });
      setSize(400, 300);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }

   public static void main(String[] args) { new Imprimante(); }
   
   private class Impression implements Printable {
      private Image image;
      
      public Impression(Image image) {
         this.image = image;
      }
      
      @Override
      public int print(Graphics g, PageFormat page, int numéro) 
      throws PrinterException {
         if (numéro>=fichiers.length) return Printable.NO_SUCH_PAGE;
         Graphics2D surface = (Graphics2D) g;
         double ratio = (double)image.getWidth(null) / image.getHeight(null);
         int largeur = (int) page.getImageableWidth();
         int hauteur = (int) (page.getImageableWidth() / ratio);
         surface.drawImage(image, (int)page.getImageableX(), (int)page.getImageableY(), largeur , hauteur, null);
         return Printable.PAGE_EXISTS;
      }
   }      
   
   private class Aperçu extends JDialog {
      private int pageCourante = 0;
      private Panneau panneau = new Panneau();
      private JToolBar barre = new JToolBar();      
      private String libellé = "Page {0}/{1}";
      private JTextField numéro = new JTextField(MessageFormat.format(libellé, pageCourante+1, pages.getNumberOfPages()));

      public Aperçu() {
         super(Imprimante.this, "Aperçu avant impression");
         add(panneau);
         barre.setFloatable(false);
         barre.add(new AbstractAction("Précédente") {
            public void actionPerformed(ActionEvent e) {
               if (pageCourante>0) {
                  pageCourante--;
                  numéro.setText(MessageFormat.format(libellé, pageCourante+1, pages.getNumberOfPages()));
                  repaint();
               }
            }
         });
         barre.add(new AbstractAction("Suivante") {
            public void actionPerformed(ActionEvent e) {
               if (pageCourante<pages.getNumberOfPages()-1) {
                  pageCourante++;
                  numéro.setText(MessageFormat.format(libellé, pageCourante+1, pages.getNumberOfPages()));
                  repaint();
               }
            }
         });
         numéro.setEditable(false);
         numéro.setHorizontalAlignment(JTextField.CENTER);
         barre.add(numéro);
         add(barre, BorderLayout.SOUTH);
         setSize(300, 300);
      }

      private class Panneau extends JComponent {
         @Override
         protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D surface = (Graphics2D) g;
            double px = page.getWidth();
            double py = page.getHeight();
            double sx = getWidth()-1;
            double sy = getHeight()-1;
            double xoff, yoff;
            double échelle;
            if (px/py < sx/sy) { // centrer horizontalement
               échelle = sy / py;
               xoff = 0.5*(sx-échelle*px);
               yoff = 0;
            }
            else { // centrer verticalement
               échelle = sx /px;
               xoff = 0;
               yoff = 0.5*(sy-échelle*py);
            }
            surface.translate(xoff, yoff);
            surface.scale(échelle, échelle);
            Rectangle2D contour = new Rectangle2D.Double(0, 0, px, py);
            surface.setPaint(Color.WHITE);
            surface.fill(contour);
            surface.setPaint(Color.BLACK);
            surface.draw(contour);
            Printable aperçu = pages.getPrintable(pageCourante);
            try {
               aperçu.print(surface, page, pageCourante);
            } 
            catch (PrinterException ex) {
               surface.draw(new Line2D.Double(0, 0, px, py));
               surface.draw(new Line2D.Double(0, px, 0, py));
            }
         }         
      }  
   }
}

 

Choix du chapitre Services d'impression

Jusqu'à présent, nous avons vu comment imprimer une image en 2D. Toutefois, l'API d'impression offre une flexibilité bien plus importante. L'API définit un certain nombre de types de données et vous permet de trouver des services d'impression capables de les imprimer. Parmi les types de données, on trouve :

  1. Des images au format GIF, JPEG ou PNG.
  2. Des documents au format texte, HTML, PostScript ou PDF.
  3. Des données de code d'impression brut.
  4. Des objets d'une classe qui implémente Printable, Pageable ou RendererImage.
Les données elles-mêmes peuvent être stockées dans une source d'octets ou de caractères, notamment dans un flux d'entrée, une URL ou un tableau. Un type de document décrit la combinaison d'une source et d'un type de données. La classe DocFavor définit un certain nombre de classes internes pour les diverses sources de données. Chaque classe interne définit des constantes, en vue de spécifier plus précisément les types de données. Par exemple la constante DocFlavor.INPUT_STREAM.GIF décrit une image lue depuis un flux d'entrée.

Source de données Type de données Type MIME

IMPUT_STREAM

URL

BYTE_ARRAY

GIF image/gif
JPEG image/jpeg
PNG image/png
POSTSCRIPT application/postscript
PDF application/PDF
TEXT_HTML_HOST text/html (utilisant l'encodage de l'hôte)
TEXT_HTML_US_ASCII text/html; charset=us-ascii
TEXT_HTML_UTF_8 text/html; charset=utf-8
TEXT_HTML_UTF_16 text/html; charset=utf-16
TEXT_HTML_UTF_16LE text/html; charset=utf-161e (little-endian)
TEXT_HTML_UTF_16BE text/html; charset=utf-161e (big-endian)
TEXT_PLAIN_HOST text/plain (utilisant l'encodage de l'hôte)
TEXT_PLAIN_US_ASCII text/plain; charset=us-ascii
TEXT_PLAIN_UTF_8 text/plain; charset=utf-8
TEXT_PLAIN_UTF_16 text/plain; charset=utf-16
TEXT_PLAIN_UTF_16LE text/plain; charset=utf-161e (little-endian)
TEXT_PLAIN_UTF_16BE text/plain; charset=utf-161e (big-endian)
PCL application/vnd.hp-PCL (langage de contrôle d'imprimante Hewlett Packard)
AUTOSENSE application/octet-stream (données d'imprimante brutes)
 
READER
STRING
CHAR_ARRAY
TEXT_HTML text/html; charset=utf-16
TEXT_PLAIN text/plain; charset=utf-16
SERVICE_FORMATTED PRINTABLE N/A
PAGEABLE N/A
RENDERABLE_IMAGE N/A

Récupération du service d'impression adapté

Supposons que vous souhaitiez imprimer une image GIF située dans un fichier. Vous devez découvrir s'il existe un service d'impression capable de gérer la tâche. La méthode lookupPrintServices() de la classe javax.print.PrintServiceLookup renvoie un tableau d'objets javax.print.PrintService capables de gérer le type de données du document.

DocFlavor type = DocFlavor.INPUT_STREAM.GIF;
PrintService[ ] services = PrintServiceLookup.lookupPrintServices(type, null);
...

Le deuxième paramètre de la méthode lookupPrintServices() vaut null pour indiquer que nous ne souhaitons pas contraindre la recherche en spécifiant des attributs d'imprimante. Nous traiterons de ces attributs ultérieurement.

L'API propose des services d'impression destinés aux types de données des documents de base, comme les images et graphisme 2D. Toutefois, si vous essayez d'imprimer du texte ou des documents HTML, la recherche renverra un tableau vide.

Si la recherche renvoie un tableau contenant plusieurs éléments, vous devez choisir parmi les services proposés. Vous pouvez appeler la méthode getName() de l'interface PrintService pour obtenir les noms des imprimantes, puis laisser l'utilisateur choisir.

Par la suite, récupérer un travail d'impression du service au travers de l'interface javax.print.DocPrintJob :

DocFlavor type = DocFlavor.INPUT_STREAM.GIF;
PrintService[ ] services = PrintServiceLookup.lookupPrintServices(type, null);
DocPrintJob travail = services[i].createPrintJob(); ...

Pour l'impression, vous avez besoin d'un objet qui implémente l'interface javax.print.Doc. L'API fournit une classe javax.print.SimpleDoc destinée à cet objectif. Le constructeur de SimpleDoc exige l'objet source des données, le type de données des documents, ainsi qu'un ensemble optionnel d'attributs. Par exemple :

InputStream flux = new FileInputStream(nomFichier);
Doc document = new SimpleDoc(flux, type, null); ...

Enfin, vous pouvez imprimer :

travail.print(document, null);

Comme avant, le paramètre null peut être remplacé par un ensemble d'attributs.
§

Vous remarquez que cette procédure d'impression diffère quelque peu de celle de la section précédente. Il n'existe aucune interaction utilisateur par le biais des boîtes de dialogue d'impression. Par exemple, vous pouvez implémenter un mécanisme d'impression côté serveur dans lequel les utilisateurs soumettent de tâches d'impression par le biais d'un formulaire Web.

Mise en oeuvre

Je vous propose de mettre une toute petite application en mode console qui permet de récupérer une photo du disque dur et de l'imprimer automatiquement en demandant à l'utilisateur de choisir son imprimante.

codage correspondant
package impression;

import java.io.*;
import javax.print.*;
import javax.swing.*;

public class Imprimante {
   public static void main(String[] args) throws IOException, PrintException {
      InputStream fichierImage = new FileInputStream("C:/Photos/Papillon.jpg");
      DocFlavor type = DocFlavor.INPUT_STREAM.JPEG;
      Doc document = new SimpleDoc(fichierImage, type, null);
      PrintService[ ] services = PrintServiceLookup.lookupPrintServices(type, null);      
      PrintService service = (PrintService)JOptionPane.showInputDialog(null,
          "Choisissez votre imprimante ?", 
          "Imprimer",  
          JOptionPane.QUESTION_MESSAGE, 
          null,
          services, 
          null);
      DocPrintJob travail = service.createPrintJob();
      travail.print(document, null);
   }
}

 

Choix du chapitre Service d'impression de flux

Un service d'impression imprime des données sur une imprimante. Un service d'impression de flux génère ces mêmes données d'impression, mais il les transmet à un flux, par exemple pour une impression retardée ou parce que leur format peut être interprété par d'autres programmes. En particulier, si ce format est de type PostScript, il est utile d'enregistrer les données d'impression dans un fichier puisque plusieurs programmes peuvent traiter ce type de fichiers.

L'API comprend un service d'impression de flux capable de produire des données PostScript à partir d'images et de Graphisques 2D. Vous pouvez utiliser ce service sur tous les systèmes, même s'il n'existe pas d'imprimantes locales.

L'énumération des services d'impression de flux est un peu plus longue que la simple localisation de services d'impression ordinaires. Il vous faut à la fois le DocFlavor de l'objet à imprimer et le type MIME de la sortie du flux. Vous obtenez alors un tableau de production de type javax.print.StreamPrintServiceFactoryau moyen de la méthode lookupStreamPrintServiceFactories() de cette même classe :
DocFlavor type = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
String typeMIME = "application/postscript";
StreamPrintServiceFactory[ ] production = StreamPrintServiceFactory.lookupStreamPrintServiceFactories(type, typeMIME);
...

La classe StreamPrintServiceFactory ne possède pas de méthodes qui vous aideraient à distinguer un facteur d'un autre, vous prendrez donc simplement production[0]. Appelez ensuite la méthode getPrintService() avec un paramètre de flus de sortie, afin d'extraire un objet de type javax.print.StreamPrintService :

OutputStream flux = new FileOutputStream(nomFichier);
StreamPrintService service = production[0].getPrintService(flux);
...

La classe StreamPrintService implémente l'interface PrintService. Ainsi, pour obtenir une impression, il vous suffit de suivre les étapes du chapitre précédent :

DocPrintJob travail = service.createPrintJob();
Doc document = new SimpleDoc(flux, type, null); travail.print(document, null);

A la différence des autres programmes d'impression, celui-ci fonctionne bien sous Linux. Vous pouvez utiliser GhostScript (GSview pour Windows) pour afficher ou imprimer le résultat.


Création d'un document PostScript
package impression;

import java.io.*;
import javax.print.*;

public class Imprimante {
   public static void main(String[] args) throws IOException, PrintException {
      InputStream fichierImage = new FileInputStream("C:/Photos/Papillon.jpg");
      DocFlavor type = DocFlavor.INPUT_STREAM.JPEG;
      Doc document = new SimpleDoc(fichierImage, type, null);
      DocFlavor typeFichier = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
      String typeMIME = "application/postscript";
      StreamPrintServiceFactory[ ] production = StreamPrintServiceFactory.lookupStreamPrintServiceFactories(typeFichier, typeMIME);     
      OutputStream fichierImpression = new FileOutputStream("C:/Photos/Papillon.ps");
      StreamPrintService service = production[0].getPrintService(fichierImpression);
      DocPrintJob travail = service.createPrintJob();
      travail.print(document, null);
   }
}  

 

Choix du chapitre Attributs d'impression

L'API du service d'impression contient un ensemble complexe d'interfaces et de classes permettant de spécifier diverses sortes d'attributs. Il existe quatre groupes importants d'attributs.

Les deux premiers spécifient des requêtes à envoyer à l'imprimante.
  1. Les attributs de requête d'impression permettent de demander des fonctions particulières pour tous les objets doc d'une tâche d'impression, comme une impression recto verso ou un format de papier particulier.
  2. Les attributs doc constituent des attributs de requêtes qui ne s'appliquent qu'à un seul objet doc.

Les deux autres contiennent des informations sur l'imprimante ainsi que sur le status de la tâche.

  1. Les attributs du service d'impression proposent des informations sur le service d'impression, comme la marque et le modèle d'imprimante ou signalent si l'imprimante accepte des tâches au moment concerné.
  2. Les attributs des tâches d'impression proposent des informations sur le statut d'une tâche d'impression particulière, et permettent notamment de savoir si la tâche est terminée.

Interfaces et classes d'attribut

Pour décrire les divers attributs, il existe une interface javax.print.attribute.Attribute contenant des sous-interfaces :

  1. PrintRequestAttribute,
  2. DocAttribute,
  3. PrintServiceAttribute,
  4. PrintJobAttribute,
  5. SupportedValuesAttribute.

Chaque classe d'attributs implémente une ou plusieurs de ces interfaces.

Par exemple, les objets de la classe javax.print.attribute.standard.Copies permettent de décrire le nombre d'exemplaires d'une impression. Cette classe implémente à la fois les interfaces PrintRequestAttribute et PrintJobAttribute. A l'évidence, une requête d'impression peut contenir une requête visant à imprimer plusieurs exemplaires. A l'inverse, un attribut de la tâche d'impression peut concerner le nombre de copies qui ont été véritablement imprimées. Ce chiffre pourrait être inférieur, par exemple dans le cas où l'imprimante affiche des limites ou parce qu'il manque du papier.

L'interface SupportedValuesAttribute signale qu'une valeur d'attribut ne reflète pas une requête réelle ou des données d'état, mais bien la capacité d'un service.

Par exemple, il existe une classe javax.print.attribute.standard.CopiesSupported qui implémente l'interface SupportedValuesAttribute. Un objet de cette classe pourrait ainsi décrire le fait qu'une imprimante accepte uniquement de 1 à 99 copies pour une impression.

Interfaces et classes d'ensembles d'attribut

Outre les interfaces et classes de chaque attribut, l'API du service d'impression définit des interfaces et des classes pour les ensembles d'attributs. Il existe d'ailleurs une super-interface javax.print.attribute.AttributeSet contenant quatre sous-interfaces :

  1. PrintRequestAttributeSet,
  2. DocAttributeSet,
  3. PrintServiceAttributeSet,
  4. PrintJobAttributeSet.

Pour chacune de ces interfaces, il existe une classe d'implémentation, qui produit les cinq classes :

  1. HashAttributeSet,
  2. HashPrintRequestAttributeSet,
  3. HasDocAttributeSet,
  4. HashPrintServiceAttributeSet,
  5. HashPrintJobAttributeSet.

Par exemple, vous construisez un ensemble d'attributs de requête d'impression comme ceci :

PrintRequestAttributeSet attributs = new HashPrintRequestAttributesSet();

Pourquoi alors disposer de toutes ces interfaces ? Elles permettent en fait de vérifier l'utilisation correcte des attributs.

Par exemple, un DocAttributeSet n'accepte que les objets qui implémentent l'interface DocAttribute. Toute tentative d'ajouter un autre attribut entraîne une erreur de type "runtime" (erreur d'exécution).

Catégorie d'attribut

Un ensemble d'attributs constitue une sorte très spécialisée de carte, dans lequel les clés sont de type Class et où les valeurs appartiennent à une classe qui implémente l'interface Attribute. Par exemple, si vous insérez l'objet new Copies(10) dans un ensemble d'attributs, sa clé est l'objet Copies.class. Cette clé est appelée la catégorie de l'attribut.

  1. L'interface Attribute déclare la méthode getCategory() qui renvoie la catégorie d'un attribut. La classe Copies définit la méthode permettant de renvoyer l'objet Copies.class, mais il n'est pas obligatoire que la catégorie soit identique à la classe de l'attribut.
  2. Lors de l'ajout d'un attribut à un ensemble d'attributs, la catégorie est extraite automatiquement. Il vous suffit d'ajouter la valeur d'attribut :

    attributs.add(new Copies(10));

  3. Pour récupérer un attribut, vous devez utiliser la catégorie sous forme de clé, par exemple :

    AttributeSet attributs = travail.getAttributes();
    Copies copies = (Copies)attributs.get(Copies.class);

  4. Enfin, les attributs sont organisés en fonction des valeurs qu'ils peuvent avoir. L'attribut Copies peut avoir une valeur d'entier. La classe Copies étend donc la classe IntegerSyntax qui s'occupe de tous les attributs à valeur entière. La méthode getValue() renvoie la valeur d'entier de l'attribut, par exemple :

    AttributeSet attributs = travail.getAttributes();
    int nombre = copies.getValue();

    Voici l'ensemble des classes qui sont à votre disposition :
    1. IntegerSyntax : qui encapsule une valeur entière.
    2. TextSyntax : qui encapsule une chaîne de caractères.
    3. DateTimeSyntax : qui encapsule une date et/ou une heure.
    4. URISyntax : qui encapsule une URI.
  5. Enfin, il existe de nombreux attributs capables de prendre un nombre fini de valeurs. Par exemple, javax.print.attribute.standard.PrintQuality possède trois paramètres :
    1. PrintQuality.DRAFT : qualité brouillon.
    2. PrintQuality.NORMAL : qualité normale.
    3. PrintQuality.HIGH : haute qualité.

Les classes d'attributs standard

Les classes d'attributs possédant un nombre fini de valeurs étendent la classe javax.print.attribute.standard.EnumSyntax (comme PrintQuality) qui fournit un certain nombre de méthodes permettant de configurer ces énumérations de manière sûre pour les caractères. Ne vous inquiétez pas du mécanisme lors de l'utilisation d'un attribut de ce type ajoutez simplement les valeurs nommées aux ensembles d'attribut :

attributs.add(PrintQuality.HIGH);

Pour vérifier la valeur d'un attribut, effectuez l'opération suivante :

if (attributs.get(PrintQuality.class) == PrintQuality.HIGH) {
...
}

Liste des attributs d'impression

Le tableau ci-dessous recense les attributs d'impression (issus du paquetage javax.print.attribute.standard). La deuxième colonne contient la superclasse de la classe d'attribut (par exemple, IntegerSyntax pour l'attribut Copies) ou l'ensemble des valeurs d'énumération pour les attributs contenant un ensemble fini de valeurs. Les quatres dernières colonnes indiquent si la classe d'attributs implémente les interfaces DocAttribute (DA), PrintJobAttribute (PJA), PrintRequestAttribute (PRA) et PrintServiceAttribute (PSA).

Attribut Superclasse ou constantes d'énumération DA PJA PRA PSA
Chromaticity MONOCHROME, COLOR X X X  
ColorSupported SUPPORTED, NOT_SUPPORTED       X
Compression COMPRESS, DEFLATE, GZIP, NONE X      
Copies IntegerSyntax   X X  
DateTimeAtCompleted DateTimeSyntax   X    
DateTimeAtCreation DateTimeSyntax   X    
DateTimeAtProcessing DateTimeSyntax   X    
Destination URISyntax   X X  
DocumentName TextSyntax X      
Fidelity FIDELITY_TRUE, FIDELITY_FALSE   X X  
Finishing NONE, STAPLE, EDGE_STITCH, BIND, SADDLE_STITCH, COVER, ... X X X  
JobHoldUntil DateTimeSyntax   X X  
JobImpressions IntegerSyntax   X X  
JobImpressionsCompleted IntegerSyntax   X    
JobKOctets IntegerSyntax   X X  
JobKOctetsProcessed IntegerSyntax   X    
JobMediaSheets IntegerSyntax   X X  
JobMediaSheetsCompleted IntegerSyntax   X    
JobMessageFromOperator TextSyntax   X    
JobName TextSyntax   X X  
JobOriginating-User-Name TextSyntax   X    
JobPriority IntegerSyntax   X X  
JobSheets STANDARD, NONE   X X  
JobState ABORTED, CANCELED, COMPLETED, PENDING, PENDING_HELD, PROCESSING, PROCESSING_STOPPED   X    
JobStateReason ABORTED_BY_SYSTEM, DOCUMENT_FORMAT_ERROR, ...        
JobStateReasons HashSet   X    
MediaName ISO_A4_WHITE, ISO_A4_TRANSPARENT, NA_LETTER_WHITE, NA_LETTER_TRANSPARENT X X X  
MediaPrintableArea MediaPrintableArea(int x, int y, int largeur, int hauteur, int unité)
=> unité ( MediaPrintableArea.INCH, MediaPrintableArea.MM)
X X X  
MediaSize ISO.A0 - ISO.A10, ISO.BO - ISO.B10, ISO.C0 - ISO.C10, NA.LETTER, NA.LEGAL, ...        
MediaSizeName ISO_A0 - ISO_A10, ISO_BO - ISO_B10, ISO_C0 - ISO_C10, NA_LETTER, NA_LEGAL, ... X X X  
MediaTray TOP, MIDDLE, BOTTOM, SIDE, ENVELOPE, LARGE_CAPACITY, MAIN, MANUAL X X X  
MultipleDocumentHandling SINGLE_DOCUMENT, DOCUMENT_NEW_SHEET, SEPARATE_DOCUMENTS_COLLATED_COPIES, SEPARATED_DOCUMENTS_UNCOLLATED_COPIES   X X  
NumberOfDocuments IntegerSyntax   X    
NumberOfInterveningJobs IntegerSyntax   X    
NumberUp IntegerSyntax X X X  
OrientationRequested PORTRAIT, LANDSCAPE, REVERSE_PORTRAIT, REVERSE_LANDSCAPE X X X  
OutputDeviceAssigned TextSyntax   X    
PageRanges SetOfInteger X X X  
PagesPerMinute IntegerSyntax       X
PagesPerMinuteColor IntegerSyntax       X
PDLOverrideSupported ATTEMPTED, NOT_ATTEMPTED       X
PresentationDirection TORIGHT_TOBOTTOM, TORIGHT_TOTOP, TOBOTTOM_TORIGHT, TOBOTTOM_TOLEFT, TOLEFT_TOBOTTOM, TOLEFT_TOTOP, TOTOP_TOLEFT X X    
PrinterInfo TextSyntax       X
PrinterIsAcceptingJobs ACCEPTING_JOBS, NOT-ACCEPTING_JOBS       X
PrinterLocation TextSyntax       X
PrinterMakeAndModel TextSyntax       X
PrinterMessageFromOperator TextSyntax       X
PrinterMoreInfo URISyntax       X
PrinterMoreInfoManufacturer URISyntax       X
PrinterName TextSyntax       X
PrinterResolution ResolutionSyntax X X X X
PrinterState PROCESSING, IDLE, STOPPED, UNKNOW       X
PrinterStateReason COVER_OPEN, FUSER_OVER_TEMP, MEDIA_JAM, ...        
PrinterStateReasons HashMap        
PrinterURI URISyntax        
PrintQuality DRAFT, NORMAL, HIGH X X X  
QueuedJobCount IntegerSyntax       X
ReferenceUriSchemesSupported FILE, FTP, GOPHER, HTTP, HTTPS, NEWS, NMTP, WAIS        
RequestingUserName TextSyntax     X  
Severity ERROR, REPORT, WARNING        
SheetCollate COLLATED, UNCOLLATED X X X  
Sides ONE_SIDED, DUPLEX, TUMBLE X X X  

Mise en oeuvre

Nous allons mettre en oeuvre ces attributs au travers de l'application que nous avons déjà mise en place lors de l'étude sur le service d'impression. Nous récupérons toujours une photo enregistrée dans un emplacement spécifique sur le disque dur, mais cette fois-ci, au lieu de prendre l'impression proposée par défaut, la photo sera imprimée en mode paysage avec une marge de 1 cm dans la partie la plus proche du bord de page.

codage correspondant avec PrintRequestAttributeSet

package impression;

import java.io.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
import javax.swing.*;

public class Imprimante {
   public static void main(String[] args) throws IOException, PrintException {
      InputStream fichierImage = new FileInputStream("C:/Photos/Papillon.jpg");
      DocFlavor type = DocFlavor.INPUT_STREAM.JPEG;
      PrintRequestAttributeSet attributs = new HashPrintRequestAttributeSet();
      attributs.add(OrientationRequested.LANDSCAPE);
      attributs.add(new MediaPrintableArea(10, 10, 190, 277, MediaPrintableArea.MM));        
      Doc document = new SimpleDoc(fichierImage, type, null);
      PrintService[ ] services = PrintServiceLookup.lookupPrintServices(type, null);      
      PrintService service = (PrintService)JOptionPane.showInputDialog(null,
          "Choisissez votre imprimante ?", 
          "Imprimer",  
          JOptionPane.QUESTION_MESSAGE, 
          null,
          services, 
          null);
      DocPrintJob travail = service.createPrintJob();
      travail.print(document, attributs);
   }
}













Autre possibilité de codage avec DocAttributeSet

package impression;

import java.io.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
import javax.swing.*;

public class Imprimante {
   public static void main(String[] args) throws IOException, PrintException {
      InputStream fichierImage = new FileInputStream("C:/Photos/Papillon.jpg");
      DocFlavor type = DocFlavor.INPUT_STREAM.JPEG;
      DocAttributeSet attributs = new HashDocAttributeSet();
      attributs.add(OrientationRequested.LANDSCAPE);
      attributs.add(new MediaPrintableArea(10, 10, 190, 277, MediaPrintableArea.MM));        
      Doc document = new SimpleDoc(fichierImage, type, attributs);
      PrintService[ ] services = PrintServiceLookup.lookupPrintServices(type, null);      
      PrintService service = (PrintService)JOptionPane.showInputDialog(null,
          "Choisissez votre imprimante ?", 
          "Imprimer",  
          JOptionPane.QUESTION_MESSAGE, 
          null,
          services, 
          null);
      DocPrintJob travail = service.createPrintJob();
      travail.print(document, null);
   }
}













 

Choix du chapitre Impression de composant de l'interface utilisateur

Depuis la version 6 de Java, il est maintenant possible de demander à imprimer le contenu d'un composant utilisateur, comme un JTextArea, un JTextPane, un JTable, etc. C'est d'une extrême simplicité puisqu'il suffit de faire appel à la méthode print() du composant concerné. Lorsque nous lançons cette méthode, la boîte de dialogue d'impression apparaît. Vous pouvez alors choisir votre imprimante, régler les marges, l'orientation, etc. Ensuite, après confirmation, l'impression se lance automatiquement en respectant l'aspect de l'original.

Exemple de mise en oeuvre

A titre d'exemple, je vous propose de voir comment imprimer avec un composant JTextArea.

codage correspondant
package impression;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.print.PrinterException;
import javax.swing.*;

public class Imprimante extends JFrame {
   private JTextArea éditeur = new JTextArea();
   private JToolBar barre = new JToolBar();     

   public Imprimante() {
      super("Impression");
      barre.add(new AbstractAction("Imprimer") {
         public void actionPerformed(ActionEvent e) {
            try {
               éditeur.print();
            } 
            catch (PrinterException ex) {
               setTitle("Problème d'impression");
            }
         }
      });
      add(barre, BorderLayout.NORTH);
      add(new JScrollPane(éditeur));
      setSize(300, 250);
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      setVisible(true);
   }   
   
   public static void main(String[] args) {  new Imprimante(); }
}