36
L'impression en java par Bruno Richeton Date de publication : 1 juin 2009 Cet article présente un retour d'expérience personnelle sur l'impression en Java. Il présente l'API officielle pour l'impression ainsi que ses limites. Il présente aussi les techniques que j'ai employées pour contourner ces limites. Ce document n'est donc pas une documentation officielle mais plutôt le fruit de recherches individuelles pour arriver à obtenir une impression de qualité en Java.

Java Print

Embed Size (px)

Citation preview

Page 1: Java Print

L'impression en java

par Bruno Richeton

Date de publication : 1 juin 2009

Cet article présente un retour d'expérience personnelle sur l'impression en Java. Il présentel'API officielle pour l'impression ainsi que ses limites. Il présente aussi les techniques quej'ai employées pour contourner ces limites.Ce document n'est donc pas une documentation officielle mais plutôt le fruit de recherchesindividuelles pour arriver à obtenir une impression de qualité en Java.

Page 2: Java Print

L'impression en java par Bruno Richeton

- 2 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

I - Remerciements....................................................................................................................................................... 3II - Avant-propos.......................................................................................................................................................... 3III - L'API java existante.............................................................................................................................................. 4

III-A - Quoi imprimer ?............................................................................................................................................4III-A-1 - L'interface Printable............................................................................................................................. 4II-A-2 - L'interface Pageable............................................................................................................................. 5

III-B - Comment imprimer ?....................................................................................................................................6III-B-1 - La classe PrinterJob............................................................................................................................6

III-B-1-a - Les PrintRequestAttribute ou requêtes d'impression..................................................................7IV - Les problèmes rencontrés.................................................................................................................................. 11

IV-A - Boîtes de configuration natives ou non.....................................................................................................11IV-B - La gestion de la résolution de l'impression...............................................................................................11IV-C - L'aperçu avant impression.........................................................................................................................12IV-D - Le découpage en bande........................................................................................................................... 12IV-E - Afficher la progression de l'impression......................................................................................................13

V - Les solutions retenues.........................................................................................................................................14V-A - Boîtes de configuration d'impression natives ou non.................................................................................14V-B - Gestion de la résolution de l'impression.................................................................................................... 14V-C - L'aperçu avant impression......................................................................................................................... 20

V-C-1 - Récupérer les informations sur l'écran.............................................................................................. 20V-C-2 - Récupérer les informations sur la feuille de papier...........................................................................20V-C-3 - Emuler l'impression sur l'écran..........................................................................................................25V-C-4 - L'impression par bande..................................................................................................................... 31V-C-5 - Affichage de la progression............................................................................................................... 32

V-C-5-a - Le thread d'impression..............................................................................................................36V-C-6 - La boîte d'impression.........................................................................................................................36

Page 3: Java Print

L'impression en java par Bruno Richeton

- 3 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

I - Remerciements

Avant tout, je tiens à faire divers remerciements :

• Au site Developpez.com d'offrir à nous autres, développeurs francophones, un formidable espace decommunications et d'informations.

• A Ricky81 pour le temps qu'il m'a consacré en tant que responsable java.• A RideKick et Xavlours pour leur relecture attentive de cet article.

II - Avant-propos

Ce document n'est pas une documentation officielle mais plutôt le fruit de recherches individuelles pour arriver àobtenir une impression de qualité en Java.

Ce document ne propose aucune solution clé en main mais uniquement des principes à appliquer pour effectuer uneimpression de qualité en Java.

Tout au long de ce document, du code est présenté. Ce code est issu d'un projet Netbeans de démonstration dont

les sources sont téléchargeables ici

Page 4: Java Print

L'impression en java par Bruno Richeton

- 4 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

III - L'API java existante

Le but de ce chapitre est de présenter succinctement l'API mise à la disposition d'un développeur voulant réaliserune impression en Java. Ce n'est en aucun cas une documentation complète.

III-A - Quoi imprimer ?

Pour l'impression, il faut tout d'abord un contenu. Pour cela, Java propose plusieurs interfaces dont Printable.

III-A-1 - L'interface Printable

Cette interface ne définit qu'une seule méthode :

Méthode de rendu d'une page à imprimer.

int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException

Paramètres• graphics: Contexte graphique pour le rendu de la page à imprimer.• pageFormat: Informations sur le format de la page à imprimer comme la taille et l'orientation.• pageIndex: Index de la page à imprimer

Valeur retournée• Retourne Printable.PAGE_EXISTS si le rendu de la page s'est bien déroulée et Printable.NO_SUCH_PAGE

si pageIndex spécifie une page non existante.

Exceptions• PrinterException : Lancée quand l'impression est interrompue.

Ainsi pour vouloir imprimer une page contenant un simple rectangle entourant la page, suivie d'une page contenantune ellipse de même dimension, il suffit d'implémenter cette interface comme ceci :

import java.awt.Color;import java.awt.Graphics;import java.awt.print.PageFormat;import java.awt.print.Printable;import java.awt.print.PrinterException;

public class PrintRectangle implements Printable { /** Constructeur par défaut de PrintRectangle */ public PrintRectangle() { }

public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { // Par défaut, retourne NO_SUCH_PAGE => la page n'existe pas int retValue = Printable.NO_SUCH_PAGE; switch(pageIndex){ case 0 : { // Dessin de la première page // Récupère la dimension de la zone imprimable double xLeft = pageFormat.getImageableX(); double yTop = pageFormat.getImageableY(); double width = pageFormat.getImageableWidth(); double height = pageFormat.getImageableHeight();

Page 5: Java Print

L'impression en java par Bruno Richeton

- 5 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

// Dessine le rectangle graphics.setColor(Color.BLACK); graphics.drawRect((int)xLeft, (int)yTop, (int)width, (int)height); // La page est valide retValue = Printable.PAGE_EXISTS; break; } case 1 : { // Dessin de la seconde page // Récupère la dimension de la zone imprimable double xLeft = pageFormat.getImageableX(); double yTop = pageFormat.getImageableY(); double width = pageFormat.getImageableWidth(); double height = pageFormat.getImageableHeight(); // Dessine l'ellipse graphics.setColor(Color.BLACK); graphics.drawOval((int)xLeft, (int)yTop, (int)width, (int)height); // La page est valide retValue = Printable.PAGE_EXISTS; break; } } return retValue; } }

Cette implémentation teste d'abord l'index de la page à imprimer transmis à la méthode print.Pour la première page (pageIndex = 0), les informations sur la zone imprimable de la page sont récupérées à l'aidedu paramètre pageFormat, puis un rectangle est dessiné à l'aide du contexte graphique transmis. La seconde pagefait la même opération en remplaçant le rectangle par une ellipse.Après chaque page rendue, la méthode retourne la constante Printable.PAGE_EXISTS pour indiquer lebon déroulement de l'impression. Dans le cas où l'index de page est invalide, il suffit de retournerPrintable.NO_SUCH_PAGE.

II-A-2 - L'interface Pageable

Dans l'exemple précédent, nous avons défini un Printable gérant 2 pages. Dans ce cas à plusieurs pages, il estpréférable d'utiliser l'interface Pageable qui représente un conteneur de Printable où chaque page peut choisir sonformat d'impression.Cette interface définit 3 méthodes.

Méthode qui retourne le nombre de pages à imprimer.

int getNumberOfPages()

Valeur retournée• Retourne le nombre de pages à imprimer ou Pageable.UNKOWN_NUMBER_OF_PAGES si ce nombre est

indéterminé.

Méthode qui retourne le format de page pour une page donnée.

PageFormat getPageFormat(int pageIndex) throws IndexOutOfBoundsException

Page 6: Java Print

L'impression en java par Bruno Richeton

- 6 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

Paramètres• pageIndex : Index de la page.

Valeur retournée• Retourne les informations sur la taille et l'orientation de la page souhaitée pour cette page.

Exceptions• IndexOutOfBoundsException : Lancée si pageIndex est erroné..

Méthode qui retourne l'objet de rendu pour une page donnée.

Printable getPrintable(int pageIndex) throws IndexOutOfBoundsException

Paramètres• pageIndex : Index de la page.

Valeur retournée• Retourne l'objet de rendu pour cette page.

Exceptions• IndexOutOfBoundsException : Lancée si pageIndex est erroné..

III-B - Comment imprimer ?

III-B-1 - La classe PrinterJob

Une fois défini le contenu à imprimer, il faut envoyer un travail d'impression à un service d'impression. Cette tâcheest réalisée par la classe PrinterJob.

import java.awt.print.PrinterException;import java.awt.print.PrinterJob;

public class Main { /** Creates a new instance of Main */ public Main() { } /** * @param args the command line arguments */ public static void main(String[] args) { // Récupère un PrinterJob PrinterJob job = PrinterJob.getPrinterJob(); // Définit son contenu à imprimer job.setPrintable(new PrintRectangle()); // Affiche une boîte de choix d'imprimante if (job.printDialog()){ try { // Effectue l'impression job.print(); } catch (PrinterException ex) { ex.printStackTrace(); } } }}

Page 7: Java Print

L'impression en java par Bruno Richeton

- 7 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

La classe PrinterJob n'a pas de constructeur accessible. De ce fait, une instance de cette classe est obtenue enappelant sa méthode statique getPrinterJob(). Une fois, cette instance obtenue il est possible :

• de lui affecter un contenu à imprimer (setPrintable, setPageable,...).• de demander à l'utilisateur de choisir l'imprimante où imprimer (printDialog,...).• d'effectuer l'impression par la méthode print.

En exécutant, ce petit programme, vous devez voir apparaître cette boîte de dialogue :

Cette boîte est la boîte standard de configuration d'impression de l'OS (ici Windows). Vous pouvez sélectionner etconfigurer l'imprimante.En appuyant sur Ok, vous imprimez 2 pages (l'une avec un rectangle et l'autre avec une ellipse) sur l'imprimante.L'appui sur Annuler, annule évidemment l'impression.

Et voilà, en quelques lignes de code, nous avons réussi à imprimer deux pages. Toutefois, toute la configurationde l'imprimante a été laissée au choix de l'utilisateur et c'est au rendu d'impression (l'implémentation de l'interfacePrintable) de s'adapter. Mais comment spécifier des préférences pour l'impression ? La solution est d'utiliser lesrequêtes d'impression ou PrintRequestAttribute.

III-B-1-a - Les PrintRequestAttribute ou requêtes d'impression

Basées sur la notion d'Attribute, les requêtes d'impression permettent aux développeurs de contrôler la configurationde l'impression.L'interface javax.print.attribute.Attribute définit deux méthodes :

Méthode qui retourne la catégorie de cet attribut.

Page 8: Java Print

L'impression en java par Bruno Richeton

- 8 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

Class< ? extends Attribute> getCategory()

Valeur retournée• Retourne la classe de la catégorie de cet attribut.

Méthode qui retourne le nom de la catégorie de cet attribut.

String getName()

Valeur retournée• Retourne le nom de la catégorie de cet attribut.

Ces attributs peuvent être rangés dans un javax.print.attribute.AttributeSet où il ne peut y avoir au mêmeinstant qu'une seule (ou zéro) valeur d'une catégorie donnée. Par exemple, si on prend la catégoriejavax.print.attribute.standard.Chromacity qui est la catégorie pour le choix entre l'impression noir & blanc etl'impression couleur. Cette catégorie ne contient que deux instances possibles : Chromacity.MONOCHROM etChromacity.COLOR. Alors ces deux choix ne peuvent cohabiter dans un même set d'attributs ; c'est l'un ou l'autreou aucun.

Cette notion étant utilisée à plusieurs endroits dans l'API d'impression de java, des familles d'attributs permettent despécialiser les attributs pour des fonctions spécifiques. Ces familles sont matérialisées par un héritage de l'interfaceAttribute :

• DocAttribute : Catégorie d'attributs de configuration d'un document (notion non abordée ici)• PrintJobAttribute : Catégorie d'attributs pour obtenir le statut ou d'autres caractéristiques d'un travail

d'impression.• PrintRequestAttribute : Catégorie d'attributs pour configurer une impression.• PrintServiceAttribute : Catégorie d'attributs pour obtenir un statut ou d'autres caractéristiques d'un service

d'impression (Un service d'impression peut être assimilé à une imprimante)• SupportedValuesAttribute : Catégorie d'attributs pour récupérer les valeurs supportées par une autre

catégorie d'attributs. Par exemple, il est possible de connaître les résolutions supportées par une imprimante.

Modifions l'exemple précédent :

import java.awt.print.PrinterException;import java.awt.print.PrinterJob;import javax.print.attribute.HashPrintRequestAttributeSet;import javax.print.attribute.standard.OrientationRequested;

/** * * */public class Main2 { /** Constructeur par défaut de Main2 */ public Main2() { } /** * @param args the command line arguments */ public static void main(String[] args) { // TODO code application logic here PrinterJob job = PrinterJob.getPrinterJob(); HashPrintRequestAttributeSet printRequestSet = new HashPrintRequestAttributeSet(); printRequestSet.add(OrientationRequested.LANDSCAPE);

Page 9: Java Print

L'impression en java par Bruno Richeton

- 9 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

job.setPrintable(new PrintRectangle()); if (job.printDialog(printRequestSet)){ try { job.print(); } catch (PrinterException ex) { ex.printStackTrace(); } } }}

Exécutons-le. La boîte suivante s'affiche :

Ce n'est plus du tout la boîte de dialogue native du système mais au contraire une boîte spécifique à Java. Si on peutencore choisir l'imprimante, le bouton propriétés est grisé. Par contre, on peut voir dans l'onglet « Mise en page »que la demande d'impression en paysage a bien été prise en compte.

Page 10: Java Print

L'impression en java par Bruno Richeton

- 10 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

De plus quand l'utilisateur clique sur imprimer, le set de requêtes transmis à la méthode printDialog est mis à jourautomatiquement pour refléter les choix de l'utilisateur. Il est donc possible à ce stade de connaître ces choix pour,par exemple, afficher un aperçu avant impression. Par contre, cette boîte ne permet plus de configurer les attributsspécifiques à une imprimante comme la sortie d'une page de garde, de l'agrafage des pages, etc...

Page 11: Java Print

L'impression en java par Bruno Richeton

- 11 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

IV - Les problèmes rencontrés

Dans le chapitre précédent, nous avons vu brièvement comment imprimer en Java. Ce chapitre énumère les différentsproblèmes et lacunes que j'ai pu rencontrer lors de l'impression.

IV-A - Boîtes de configuration natives ou non

Nous avons vu que la méthode printDialog affiche deux boîtes différentes en fonction de la présence ou non d'unset de requêtes. Ne peut-on pas spécifier un set de requêtes et conserver la boîte de configuration native qui estplus complète ?

IV-B - La gestion de la résolution de l'impression

En consultant la documentation de PageFormat, on constate que toutes les méthodes retournent des mesures en1/72 de pouce soit du 72 DPI.Faisons le petit test suivant :

import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Rectangle;import java.awt.print.PageFormat;import java.awt.print.Printable;import java.awt.print.PrinterException;

public class PrintResolution implements Printable { /** Constructeur par défaut de PrintResolution */ public PrintResolution () { }

public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { int retValue = Printable.NO_SUCH_PAGE; if (pageIndex == 0){ Graphics2D g2d = (Graphics2D)graphics; // Device String text = "Taille du device : " + g2d.getDeviceConfiguration().getBounds().toString(); graphics.drawString(text, (int)(pageFormat.getImageableX()), (int)(pageFormat.getImageableY()+pageFormat.getImageableHeight()/3.0));

// clipping text = "Taille du clipping : " + g2d.getClipBounds().toString(); graphics.drawString(text, (int)(pageFormat.getImageableX()), (int)(pageFormat.getImageableY()+pageFormat.getImageableHeight()/2.0)); // imageable Rectangle rect = new Rectangle((int)pageFormat.getImageableX(), (int)pageFormat.getImageableY(), (int)pageFormat.getImageableWidth(), (int)pageFormat.getImageableHeight()); text = "Taille de l'imageable : " + rect.toString(); graphics.drawString(text, (int)(pageFormat.getImageableX()), (int)(pageFormat.getImageableY()+2.0*pageFormat.getImageableHeight()/3.0)); retValue = PAGE_EXISTS; } return retValue; }}

Pour une impression configurée en 600 DPI, nous pouvons avoir le résultat suivant (en fonction des marges acceptéespar l'imprimante) :

Page 12: Java Print

L'impression en java par Bruno Richeton

- 12 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

• Taille du device : java.awt.Rectangle[x=0,y=0,width=4960,height=7015]•• Taille du clipping : java.awt.Rectangle[x=0,y=0,width=595,height=842]•• Taille de l'imageable : java.awt.Rectangle[x=0,y=0,width=594,height=841]

Ainsi le contexte graphique transmis à la méthode print est toujours configuré en 72 DPI, c'est-à-dire qu'une ligne de72 pixels de long aura pour longueur un pouce sur la feuille. Pourtant l'utilisateur a demandé une impression en 600DPI. Comment faire pour respecter cette résolution ?

Ce choix de résolution fixe à 72 DPI s'explique facilement pour la gestion des polices de caractères ; toutes les taillesde police en java étant exprimées en points c'est-à-dire en 1/72 de pouce. Cette gestion de la taille de police poseun problème si l'affichage a lieu à une autre résolution que les fameux 72 DPI.

Ainsi, pour un affichage en 72 DPI, une police de 12 points a donc une hauteur de 12 pixels. Les contextes graphiquesde java considèrent automatiquement que la résolution d'affichage est de 72 DPI. Pour obtenir un affichage à unerésolution autre, il faudra donc retailler le texte en fonction ; dans une résolution de 600 DPI, une police de 12 pointsa une hauteur de :1 pouce => 600 pixels => 72 points0.1667 pouces => 100 pixels => 12 points

Remarque : Ce problème de taille de caractères survient même à l'écran. En effet, sousWindows, la résolution native de l'écran est du 96 DPI voire plus alors que les contextesjava se considèrent en 72 DPI. C'est pourquoi une police de 12 points java est plus petitequ'une police 12 points système comme y répond l'extrait de la FAQ de Sun ci-dessous :Q: Why does (eg) a 10 pt font in Java applications appear to have a different sizefrom the same font at 10pt in a native application?A: Conversion from the size in points into device pixels depends on device resolution asreported by the platform APIs. Java 2D defaults to assuming 72 dpi. Platform defaults vary.Mac OS also uses 72 dpi. Linux desktops based on GTK (Gnome) or Qt (KDE) typicallydefault to 96 dpi and let the end-user customise what they want to use. Windows defaultsto 96 dpi (VGA resolution) and also offers 120 dpi (large fonts size) and lets users furtherspecify a custom resolution. So a couple of things can now be seen* The DPI reported by platform APIs likely has no correspondence to the true DPI of thedisplay device* Its unlikely that Java 2D's default matches the platform default.So a typical results is that for Window's default 96 DPI that a 10 pt font in a Java applicationis 72/96 of the size of the native counterpart.Note that Swing's Windows and GTK L&Fs do scale fonts based on the system DPIto match the desktop. If you want to do the same in your application you can calljava.awt.Toolkit.getScreenResolution() and use this to apply a simple scale to the size youspecify for fonts.http://java.sun.com/products/java-media/2D/reference/faqs/index.html#Q_What_are_the_different_ways_th

IV-C - L'aperçu avant impression

Java ne propose aucune API pour afficher un aperçu avant impression c'est-à-dire émuler l'affichage d'une pageimprimée en 600 DPI monochrome par exemple, sur un écran en 96 DPI couleur.

IV-D - Le découpage en bande

Une page A4 en 600 DPI atteint des dimensions de l'ordre de 5000x7000 pixels. Ces tailles peuvent être trèscoûteuses en mémoire pour imprimer des images par exemple. L'impression doit donc découper en bandes l'imagefinale pour se cantonner à des dimensions négligeables en mémoire.

Page 13: Java Print

L'impression en java par Bruno Richeton

- 13 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

IV-E - Afficher la progression de l'impression

Une impression n'est pas une opération rapide. Il faut donc informer l'utilisateur de la progression de celle-ci. Il n'estpas superflu non plus d'offrir à l'utilisateur la possibilité d'annuler l'impression.

Page 14: Java Print

L'impression en java par Bruno Richeton

- 14 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

V - Les solutions retenues

V-A - Boîtes de configuration d'impression natives ou non

En effectuant un peu de débogage sur les printJob après un appel à la méthode printDialog, j'ai découvertqu'un attribut était ajouté dans les requêtes et qui visiblement pilote la boîte affichée. C'est l'attribut de catégoriesun.print.DialogTypeSelection. Cette catégorie possède deux valeurs possibles :

• DialogTypeSelection.COMMON : Pour les boîtes d'impression swing.• DialogTypeSelection.NATIVE : Pour les boîtes d'impression natives.

Ainsi, en modifiant un peu le code comme ceci :

import java.awt.print.PrinterException;import java.awt.print.PrinterJob;import javax.print.attribute.HashPrintRequestAttributeSet;import javax.print.attribute.standard.OrientationRequested;import sun.print.DialogTypeSelection;

public class Main4 { /** Constructeur par défaut de Main2 */ public Main4() { } /** * @param args the command line arguments */ public static void main(String[] args) { PrinterJob job = PrinterJob.getPrinterJob(); HashPrintRequestAttributeSet printRequestSet = new HashPrintRequestAttributeSet(); printRequestSet.add(OrientationRequested.LANDSCAPE); printRequestSet.add(DialogTypeSelection.NATIVE); job.setPrintable(new PrintRectangle()); if (job.printDialog(printRequestSet)){ try { job.print(); } catch (PrinterException ex) { ex.printStackTrace(); } } }}

Nous obtenons la boîte d'impression native au prix de quelques warnings lors de la compilation :warning: sun.print.DialogTypeSelection is Sun proprietary API and may be removed in a future releaseAu développeur de voir s'il accepte un jour de revoir son code quand celui-ci ne sera plus supporté par Sun.

V-B - Gestion de la résolution de l'impression

Comme nous avons vu dans la partie de présentation de cette problématique, le contexte graphique transmis à laméthode print d'un printable est automatiquement configuré en 72 DPI. Toutefois, en interrogeant la configuration dece contexte (getDeviceConfiguration), il est possible d'obtenir la vraie résolution. Comment passer de la configuration72 DPI à la configuration résolution réelle ?

Page 15: Java Print

L'impression en java par Bruno Richeton

- 15 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

Tout est dans la transformation associée au contexte graphique. Par défaut, elle est donc configurée pour une sortieen 72 DPI en respectant les marges d'impression ainsi que l'orientation de la feuille. Le développeur doit donc toutreconfigurer :

• Associer au contexte graphique une transformation identité pour passer en mode 1 unité graphique = 1pixels.

• Effectuer une éventuelle rotation pour respecter l'orientation de la feuille (portrait/paysage)• Effectuer une translation pour respecter les marges d'impression

import java.awt.Color;import java.awt.Font;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Rectangle;import java.awt.geom.AffineTransform;import java.awt.print.PageFormat;import java.awt.print.Printable;import java.awt.print.PrinterException;

public class PrintRectangleRes implements Printable { // Résolution d'un contexte graphique Java private static final double JAVA_DPI = 72.0; // Police d'affichage du texte private Font font = new Font(Font.MONOSPACED, Font.BOLD, 16); /** Constructeur par défaut de PrintRectangle */ public PrintRectangleRes() { } /** * Méthode qui restaure le contexte graphique dans sa résolution réelle. * * @param graphics le contexte graphique * @param pageFormat information sur le format de la page * @return le rectangle de la zone imprimable dans la résolution réelle */ protected Rectangle restoreRealDpi(Graphics2D graphics, PageFormat pageFormat){ Rectangle retValue = new Rectangle(); // Détermine la résolution réelle Rectangle deviceBounds = graphics.getDeviceConfiguration().getBounds(); double pageWidth72Dpi = pageFormat.getWidth(); double pageHeight72Dpi = pageFormat.getHeight(); double widthResolution = (JAVA_DPI * deviceBounds.getWidth())/pageWidth72Dpi; double heightResolution = (JAVA_DPI * deviceBounds.getHeight())/pageHeight72Dpi; // Détermine les dimensions réelles de la zone imprimable double realImageableX = (pageFormat.getImageableX()*widthResolution)/ JAVA_DPI; double realImageableWidth = (pageFormat.getImageableWidth()*widthResolution)/ JAVA_DPI; double realImageableY = (pageFormat.getImageableY()*heightResolution)/ JAVA_DPI; double realImageableHeight = (pageFormat.getImageableHeight()*heightResolution)/ JAVA_DPI; // Modifie la transformation du contexte graphique graphics.setTransform(new AffineTransform()); // Passe en résolution réelle switch (pageFormat.getOrientation()){ case PageFormat.LANDSCAPE : { // Les marges retournées par pageFormat prennent en compte la rotation // Il faut les inverser double temp = realImageableX; realImageableX = realImageableY; realImageableY = temp; temp = realImageableWidth; realImageableWidth = realImageableHeight;

Page 16: Java Print

L'impression en java par Bruno Richeton

- 16 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

realImageableHeight = temp; // Effectue la rotation graphics.rotate(-Math.PI / 2.0); // Translation pour s'aligner sur les marges graphics.translate(-realImageableWidth + realImageableX, realImageableY); break; } case PageFormat.REVERSE_LANDSCAPE : { // Les marges retournées par pageFormat prennent en compte la rotation // Il faut les inverser double temp = realImageableX; realImageableX = realImageableY; realImageableY = temp; temp = realImageableWidth; realImageableWidth = realImageableHeight; realImageableHeight = temp; // Effectue la rotation graphics.rotate(Math.PI / 2.0); // Translation pour s'aligner sur les marges graphics.translate(realImageableX, realImageableY - realImageableHeight); break; } default : { // Mode portrait // Translation pour s'aligner sur les marges graphics.translate(realImageableX, realImageableY); } } retValue.x = (int)Math.ceil(realImageableX); retValue.y = (int)Math.ceil(realImageableY); retValue.width = (int)Math.floor(realImageableWidth); retValue.height = (int)Math.floor(realImageableHeight);

return retValue; }

public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { // Par défaut, retourne NO_SUCH_PAGE => la page n'existe pas int retValue = Printable.NO_SUCH_PAGE; switch(pageIndex){ case 0 : { // Restaure la résolution réelle Rectangle margin = restoreRealDpi((Graphics2D)graphics, pageFormat); // Dessine le rectangle graphics.setColor(Color.BLACK); graphics.drawRect(0, 0, margin.width, margin.height); // Affiche les marges graphics.setFont(font); graphics.drawString(margin.toString(), 0, margin.height/2); // La page est valide retValue = Printable.PAGE_EXISTS; break; } case 1 : { // Dessin de la seconde page // Restaure la résolution réelle Rectangle margin = restoreRealDpi((Graphics2D)graphics, pageFormat); // Dessine le rectangle graphics.setColor(Color.BLACK); graphics.drawOval(0, 0, margin.width, margin.height);

Page 17: Java Print

L'impression en java par Bruno Richeton

- 17 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

// Affiche les marges graphics.setFont(font); graphics.drawString(margin.toString(), 0, margin.height/2); // La page est valide retValue = Printable.PAGE_EXISTS; break; } } return retValue; }}

L'implémentation d'un Printable ci-dessus ajoute la méthode restoreRealDpi qui s'occupe de configurer le contextegraphique en résolution réelle.Si l'affichage des figures géométriques est correct, l'affichage du texte dépend fortement de la résolution. Plusla résolution est élevée, plus le texte est petit alors que dans le code sa taille est constante (16 points). Cefonctionnement est tout à fait normal pour Java puisque celui-ci considère toujours que la résolution est de 72 DPIpour l'affichage du texte. Il faut donc fournir des méthodes pour afficher du texte à une résolution donnée. C'est làque les choses se compliquent.En interne, les contextes graphiques java utilisent une transformation spécifique pour l'affichage du texte,malheureusement cette transformation est en lecture seule. Il nous faut donc effectuer le rendu du texte nous même.Heureusement, les polices java nous permettent de récupérer des GlyphVector qu'il est possible d'afficher ensuitecomme si cela était une géométrie quelconque.

import java.awt.Color;import java.awt.Font;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Rectangle;import java.awt.font.FontRenderContext;import java.awt.font.GlyphVector;import java.awt.font.TextLayout;import java.awt.geom.AffineTransform;import java.awt.geom.Point2D;import java.awt.print.PageFormat;import java.awt.print.Printable;import java.awt.print.PrinterException;

public class PrintRectangleResText implements Printable { // Résolution d'un contexte graphique Java private static final double JAVA_DPI = 72.0; // Police d'affichage du texte private Font font = new Font(Font.MONOSPACED, Font.BOLD, 16); // Résolution de l'impression private int resolution = 72; /** Constructeur par défaut de PrintRectangle */ public PrintRectangleResText() { } /** * Méthode qui restaure le contexte graphique dans sa résolution réelle. * * @param graphics le contexte graphique * @param pageFormat information sur le format de la page * @return le rectangle de la zone imprimable dans la résolution réelle */ protected Rectangle restoreRealDpi(Graphics2D graphics, PageFormat pageFormat){ Rectangle retValue = new Rectangle(); // Détermine la résolution réelle Rectangle deviceBounds = graphics.getDeviceConfiguration().getBounds();

Page 18: Java Print

L'impression en java par Bruno Richeton

- 18 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

double pageWidth72Dpi = pageFormat.getWidth(); double pageHeight72Dpi = pageFormat.getHeight(); double widthResolution = (JAVA_DPI * deviceBounds.getWidth())/pageWidth72Dpi; double heightResolution = (JAVA_DPI * deviceBounds.getHeight())/pageHeight72Dpi; // Détermine la résolution pour l'affichage du texte resolution = (int)Math.round((widthResolution + heightResolution)/2.0); // Détermine les dimensions réelle de la zone imprimable double realImageableX = (pageFormat.getImageableX()*widthResolution)/ JAVA_DPI; double realImageableWidth = (pageFormat.getImageableWidth()*widthResolution)/ JAVA_DPI; double realImageableY = (pageFormat.getImageableY()*heightResolution)/ JAVA_DPI; double realImageableHeight = (pageFormat.getImageableHeight()*heightResolution)/ JAVA_DPI; // Modifie la transformation du contexte graphique graphics.setTransform(new AffineTransform()); // Passe en résolution réelle switch (pageFormat.getOrientation()){ case PageFormat.LANDSCAPE : { // Les marges retournées par pageFormat prennent en compte la rotation // Il faut les inverser double temp = realImageableX; realImageableX = realImageableY; realImageableY = temp; temp = realImageableWidth; realImageableWidth = realImageableHeight; realImageableHeight = temp; // effectue la rotation graphics.rotate(-Math.PI / 2.0); // Translation pour s'aligner sur les marges graphics.translate(-realImageableWidth + realImageableX, realImageableY); break; } case PageFormat.REVERSE_LANDSCAPE : { // Les marges retournées par pageFormat prennent en compte la rotation // Il faut les inverser double temp = realImageableX; realImageableX = realImageableY; realImageableY = temp; temp = realImageableWidth; realImageableWidth = realImageableHeight; realImageableHeight = temp; // effectue la rotation graphics.rotate(Math.PI / 2.0); // Translation pour s'aligner sur les marges graphics.translate(realImageableX, realImageableY - realImageableHeight); break; } default : { // Mode portrait // Translation pour s'aligner sur les marges graphics.translate(realImageableX, realImageableY); } } retValue.x = (int)Math.ceil(realImageableX); retValue.y = (int)Math.ceil(realImageableY); retValue.width = (int)Math.floor(realImageableWidth); retValue.height = (int)Math.floor(realImageableHeight); return retValue; } /** * Affiche du texte indépendamment de la résolution * * @param graphics le contexte graphics * @param text le texte à afficher

Page 19: Java Print

L'impression en java par Bruno Richeton

- 19 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

* @param x l'abscisse où placer le texte * @param y l'ordonnée où placer le texte */ public void printText(Graphics2D graphics, String text, int x, int y){ Font currentFont = graphics.getFont(); // Calcul l'échelle du texte double fontScale = ((double)(resolution*currentFont.getSize()))/(JAVA_DPI * 72.0); // transformation de la police AffineTransform fontShapeTransform = new AffineTransform(); fontShapeTransform.setToScale(fontScale, fontScale); // Font de récupération de glyph vector Font computeFont = new Font(currentFont.getName(), currentFont.getStyle(), 72); // Font de calcul de largeur d'un caractère => ignore le flag italique Font sizeFont = computeFont; if (font.isItalic()) { sizeFont = new Font(currentFont.getFontName(), currentFont.getStyle()-Font.ITALIC, 72); } // La position courante du texte Point2D.Double textPos = new Point2D.Double(x, y); // Récupère le contexte de rendu de police FontRenderContext frc = graphics.getFontRenderContext(); // On boucle sur chaque caractères char[] carIterator = new char[1]; int textLength = text.length(); for (int i = 0; i < textLength; i++) { // récupère le caractère courant text.getChars(i, i+1, carIterator, 0); // Récupère le glyph de ce caractère pour la police de calcul GlyphVector glyph = computeFont.createGlyphVector(frc, carIterator); graphics.translate(textPos.x, textPos.y); glyph.setGlyphTransform(0, fontShapeTransform); graphics.drawGlyphVector(glyph, 0.f, 0.f); graphics.translate(-textPos.x, -textPos.y); // Incrémente la position du texte TextLayout layout = new TextLayout(new String(carIterator), sizeFont, frc); textPos.x += (double)layout.getAdvance()* fontScale; } } public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { // Par défaut, retourne NO_SUCH_PAGE => la page n'existe pas int retValue = Printable.NO_SUCH_PAGE; switch(pageIndex){ case 0 : { Graphics2D g2d = (Graphics2D)graphics; // Restaure la résolution réelle Rectangle margin = restoreRealDpi(g2d, pageFormat); // Dessine le rectangle graphics.setColor(Color.BLACK); graphics.drawRect(0, 0, margin.width, margin.height); // Affiche les marges graphics.setFont(font); printText(g2d, margin.toString(), 0, margin.height/2); // La page est valide retValue = Printable.PAGE_EXISTS; break; }

Page 20: Java Print

L'impression en java par Bruno Richeton

- 20 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

case 1 : { // Dessin de la seconde page Graphics2D g2d = (Graphics2D)graphics; // Restaure la résolution réelle Rectangle margin = restoreRealDpi(g2d, pageFormat); // Dessine le rectangle graphics.setColor(Color.BLACK); graphics.drawOval(0, 0, margin.width, margin.height); // Affiche les marges graphics.setFont(font); printText(g2d, margin.toString(), 0, margin.height/2); // La page est valide retValue = Printable.PAGE_EXISTS; break; } } return retValue; }}

Dans l'exemple ci-dessus, toute la gestion de l'affichage du texte est contenue dans la méthode printText.Le principe de cette méthode est de récupérer un GlyphVector d'une fonte de référence. Ici, la police de référence aune taille de 72 points pour une résolution de 72 DPI. Ensuite une transformation de mise à l'échelle est appliquéeà cette fonte de référence pour l'afficher à la taille voulue.Dans un premier temps, le facteur de mise à l'échelle est déterminé. Nous souhaitons afficher une police de taillefontSize à une résolution donnée resolution à partir d'une police de taille 72 points pour une résolution de 72 DPI.Ainsi :fontScale = (fontSize * resolution)/ (72 * 72)Ensuite pour chaque caractère du texte à afficher, on récupère son GlyphVector pour la police de référence que l'onpositionne et retaille à la taille voulue.Pour calculer l'espacement entre chaque caractère, un TextLayout est utilisé. A noter, qu'il faut ignorer l'attribut italiquequi fausse ce calcul.

V-C - L'aperçu avant impression

L'aperçu avant impression consiste à afficher à l'écran une image destinée à être rendue sur une feuille de papier.Pour cela, il nous faut :

• Des informations sur la dimension et la résolution de la fenêtre d'affichage de l'écran.• Des informations sur la dimension et la résolution de la feuille de papier.• Emuler l'affichage de la feuille de papier à l'écran

V-C-1 - Récupérer les informations sur l'écran

L'aperçu sera rendu grâce à un JComponent et sa méthode paint. La récupération de la dimension de ce composantne pose donc aucun souci. Il suffit d'appeler la méthode getSize(). Pour la résolution écran, elle est tout simplementretournée par un appel à java.awt.Toolkit.getDefaultToolkit().getScreenResolution().

V-C-2 - Récupérer les informations sur la feuille de papier

Ces informations sont directement issues du choix de l'utilisateur pour la configuration de l'imprimante. Nous avonsvu qu'il est possible de récupérer les choix de l'utilisateur après une configuration de l'imprimante grâce aux requêtesd'impression. Il nous suffit d'encapsuler les paramètres importants à nos yeux dans une instance d'une classePrintParameters que nous mettrons à jour après chaque modification de la configuration d'impression.

Page 21: Java Print

L'impression en java par Bruno Richeton

- 21 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

import java.awt.geom.Rectangle2D;import java.awt.print.PageFormat;import java.awt.print.PrinterException;import java.awt.print.PrinterJob;import java.util.logging.Level;import java.util.logging.Logger;import javax.print.PrintService;import javax.print.PrintServiceLookup;import javax.print.attribute.HashPrintRequestAttributeSet;import javax.print.attribute.PrintRequestAttributeSet;import javax.print.attribute.standard.Chromaticity;import javax.print.attribute.standard.MediaPrintableArea;import javax.print.attribute.standard.OrientationRequested;import javax.print.attribute.standard.PrinterResolution;

/** * * */public class PrintParameters { /** * Les dimensions de la feuille de papier en inches */ private Rectangle2D.Double paperArea; /** * Les marges imprimables en inches */ private Rectangle2D.Double printableArea; /** * La résolution en DPI */ private int DPI; /** * L'orientation du papier */ private OrientationRequested orientation; /** * Les attributs de la requête d'impression courante */ private PrintRequestAttributeSet attributes; /** * Le service d'impression (ou imprimante) sélectionné */ private PrintService printService; /** * Indique si l'impression est en couleur ou non */ private boolean monochrom; public PrintParameters(){ this(new HashPrintRequestAttributeSet(), PrintServiceLookup.lookupDefaultPrintService()); } public PrintParameters(PrintRequestAttributeSet attributesSet, PrintService printService){ try { this.attributes = attributesSet; this.printService = printService; // Récupère la résolution PrinterResolution res = (PrinterResolution) attributesSet.get(PrinterResolution.class); if (res == null) { res = (PrinterResolution) printService.getDefaultAttributeValue(PrinterResolution.class); } DPI = res.getResolution(PrinterResolution.DPI)[0];

Page 22: Java Print

L'impression en java par Bruno Richeton

- 22 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

// Récupère l'orientation orientation = (OrientationRequested)attributesSet.get(OrientationRequested.class); if (orientation == null){ orientation = (OrientationRequested)printService.getDefaultAttributeValue(OrientationRequested.class); } // Détermine les dimensions de la feuille et des marges PrinterJob printerJob = PrinterJob.getPrinterJob(); printerJob.setPrintService(printService); // Récupère les marges physiques de l'imprimante float [] physicalMargin; MediaPrintableArea printableZone = (MediaPrintableArea)attributesSet.get(MediaPrintableArea.class); if (printableZone == null){ printableZone = (MediaPrintableArea)printService.getDefaultAttributeValue(MediaPrintableArea.class); } PageFormat pageFormat = printerJob.getPageFormat(attributesSet); double paperWidth = pageFormat.getWidth() / 72.0; double paperHeight = pageFormat.getHeight() / 72.0; if (printableZone == null){ physicalMargin = new float[4]; physicalMargin[0] = 0.0f; physicalMargin[1] = 0.0f; physicalMargin[2] = (float)paperWidth; physicalMargin[3] = (float)paperHeight; } else { physicalMargin = printableZone.getPrintableArea(MediaPrintableArea.INCH); } if (orientation.equals(OrientationRequested.LANDSCAPE) ||orientation.equals(OrientationRequested.REVERSE_LANDSCAPE)){ // Inversion des marges physiques float temp = physicalMargin[0]; physicalMargin[0] = physicalMargin[1]; physicalMargin[1] = temp; temp = physicalMargin[2]; physicalMargin[2] = physicalMargin[3]; physicalMargin[3] = temp; } // Calcul des différences physicalMargin[2] = (float)(paperWidth - (physicalMargin[2] + physicalMargin[0])); physicalMargin[3] = (float)(paperHeight - (physicalMargin[3] + physicalMargin[1])); double xMargin = (pageFormat.getImageableX() / 72.0) + physicalMargin[0]; double yMargin = (pageFormat.getImageableY() / 72.0) + physicalMargin[1]; double imageWidth = (pageFormat.getImageableWidth() / 72.0) - (physicalMargin[0] + physicalMargin[2]); double imageHeight = (pageFormat.getImageableHeight() / 72.0) - (physicalMargin[1] + physicalMargin[3]); paperArea = new Rectangle2D.Double(0.0, 0.0, paperWidth, paperHeight); printableArea = new Rectangle2D.Double(xMargin, yMargin, imageWidth, imageHeight); // La gestion de la couleur Chromaticity chromaticity = (Chromaticity) attributesSet.get(Chromaticity.class); if (chromaticity == null) { chromaticity = (Chromaticity) printService.getDefaultAttributeValue(Chromaticity.class); } monochrom = chromaticity.equals(Chromaticity.MONOCHROME); } catch (PrinterException ex) { Logger.getLogger(PrintParameters.class.getName()).log(Level.SEVERE, null, ex); } } /**

Page 23: Java Print

L'impression en java par Bruno Richeton

- 23 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

* Retourne les dimensions de la feuille de papier en inches * * @return les dimensions de la feuille en inches */ public Rectangle2D.Double getPaperArea() { return paperArea; } /** * Retourne la zone imprimable en inches * @return la zone imprimable en inches */ public Rectangle2D.Double getPrintableArea() { return printableArea; } /** * Retourne la résolution de l'impression en DPI * @return la résolution de l'impression en DPI */ public int getDPI() { return DPI; } /** * Indique si l'impression est en couleur ou monochrome * @return true si l'impression est monochrome */ public boolean isMonochrom() { return monochrom; } /** * Retourne l'orientation de la feuille * @return l'orientation de la feuille */ public OrientationRequested getOrientation() { return orientation; } /** * Retourne les requêtes d'impression courantes * * @return les requêtes d'impression courantes */ protected PrintRequestAttributeSet getAttributes() { return attributes; } /** * Retourne le service d'impression sélectionné * * @return le service d'impression sélectionné */ protected PrintService getPrintService() { return printService; }}

Cette classe est un simple conteneur non mutable où tous les champs sont initialisés dans le constructeur. Ellepossède deux constructeurs :

• Un constructeur qui utilise un service d'impression et un set de requête.• Un constructeur par défaut qui appelle le précédent en lui passant le service d'impression par défaut et un set

de requêtes vide.

Cette classe contient :

Page 24: Java Print

L'impression en java par Bruno Richeton

- 24 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

• La dimension de la feuille en pouces• Les marges d'impression en pouces• L'orientation de la feuille• La résolution en DPI• Un indicateur d'impression couleur ou noir et blanc

Dans cette classe, nous voyons comment récupérer des informations à partir d'un service d'impression et d'un setde requêtes. Tous les attributs répondant sur le même principe, nous décrivons seulement la récupération de larésolution.

PrinterResolution res = (PrinterResolution) attributesSet.get(PrinterResolution.class);if (res == null) { res = (PrinterResolution) printService.getDefaultAttributeValue(PrinterResolution.class);}int DPI = res.getResolution(PrinterResolution.DPI)[0];

On teste la présence d'un attribut de la catégorie PrinterResolution dans le set de requêtes. Si aucun attribut n'existe,alors on interroge le service d'impression pour récupérer sa résolution par défaut.

Ensuite la résolution est extraite de l'attribut retourné.

A partir de cette classe, il est facile de stocker les paramètres d'impression.

import java.awt.print.PrinterException;import java.awt.print.PrinterJob;import javax.print.attribute.HashPrintRequestAttributeSet;import javax.print.attribute.standard.OrientationRequested;import sun.print.DialogTypeSelection;

/** * * */public class Main7 { /** Constructeur par défaut de Main7 */ public Main7() { } /** * @param args the command line arguments */ public static void main(String[] args) { PrinterJob job = PrinterJob.getPrinterJob(); HashPrintRequestAttributeSet printRequestSet = new HashPrintRequestAttributeSet(); printRequestSet.add(DialogTypeSelection.NATIVE); job.setPrintable(new PrintRectangleResText()); if (job.printDialog(printRequestSet)){ PrintParameters params = new PrintParameters(printRequestSet, job.getPrintService()); System.out.println("L'utilisateur a choisi d'imprimer sur " + job.getPrintService().getName()); System.out.println(String.format(" la résolution est de : %s DPI",params.getDPI())); if (params.getOrientation().equals(OrientationRequested.PORTRAIT)){ System.out.println(" mode PORTRAIT"); } else { System.out.println(" mode PAYSAGE"); } } }}

Page 25: Java Print

L'impression en java par Bruno Richeton

- 25 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

V-C-3 - Emuler l'impression sur l'écran

A ce stade, nous disposons de toutes les informations nécessaires pour effectuer un aperçu avant impression.

import java.awt.Color;import java.awt.Dimension;import java.awt.Font;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Rectangle;import java.awt.Toolkit;import java.awt.font.FontRenderContext;import java.awt.font.GlyphVector;import java.awt.font.TextLayout;import java.awt.geom.AffineTransform;import java.awt.geom.Point2D;import java.awt.geom.Rectangle2D;import java.awt.print.PageFormat;import java.awt.print.Printable;import java.awt.print.PrinterException;

public class PrintRectangleResTextPreview implements Printable { // Résolution d'un contexte graphique Java private static final double JAVA_DPI = 72.0; // Police d'affichage du texte private Font font = new Font(Font.MONOSPACED, Font.BOLD, 16); // Résolution de l'impression private int resolution = 72; /** Constructeur par défaut de PrintRectangle */ public PrintRectangleResTextPreview() { } /** * Méthode qui restaure le contexte graphique dans sa résolution réelle. * * @param graphics le contexte graphique * @param pageFormat information sur le format de la page * @return le rectangle de la zone imprimable dans la résolution réelle */ protected Rectangle restoreRealDpi(Graphics2D graphics, PageFormat pageFormat){ Rectangle retValue = new Rectangle(); // Détermine la résolution réelle Rectangle deviceBounds = graphics.getDeviceConfiguration().getBounds(); double pageWidth72Dpi = pageFormat.getWidth(); double pageHeight72Dpi = pageFormat.getHeight(); double widthResolution = (JAVA_DPI * deviceBounds.getWidth())/pageWidth72Dpi; double heightResolution = (JAVA_DPI * deviceBounds.getHeight())/pageHeight72Dpi; // Détermine la résolution pour l'affichage du texte resolution = (int)Math.round((widthResolution + heightResolution)/2.0); // Détermine les dimensions réelle de la zone imprimable double realImageableX = (pageFormat.getImageableX()*widthResolution)/ JAVA_DPI; double realImageableWidth = (pageFormat.getImageableWidth()*widthResolution)/ JAVA_DPI; double realImageableY = (pageFormat.getImageableY()*heightResolution)/ JAVA_DPI; double realImageableHeight = (pageFormat.getImageableHeight()*heightResolution)/ JAVA_DPI; // Modifie la transformation du contexte graphique graphics.setTransform(new AffineTransform()); // Passe en résolution réelle switch (pageFormat.getOrientation()){

Page 26: Java Print

L'impression en java par Bruno Richeton

- 26 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

case PageFormat.LANDSCAPE : { // Les marges retournées par pageFormat prennent en compte la rotation // Il faut les inverser double temp = realImageableX; realImageableX = realImageableY; realImageableY = temp; temp = realImageableWidth; realImageableWidth = realImageableHeight; realImageableHeight = temp; // Effectue la rotation graphics.rotate(-Math.PI / 2.0); // Translation pour s'aligner sur les marges graphics.translate(-realImageableWidth + realImageableX, realImageableY); break; } case PageFormat.REVERSE_LANDSCAPE : { // Les marges retournées par pageFormat prennent en compte la rotation // Il faut les inverser double temp = realImageableX; realImageableX = realImageableY; realImageableY = temp; temp = realImageableWidth; realImageableWidth = realImageableHeight; realImageableHeight = temp; // Effectue la rotation graphics.rotate(Math.PI / 2.0); // Translation pour s'aligner sur les marges graphics.translate(realImageableX, realImageableY - realImageableHeight); break; } default : { // Mode portrait // Translation pour s'aligner sur les marges graphics.translate(realImageableX, realImageableY); } } retValue.x = (int)Math.ceil(realImageableX); retValue.y = (int)Math.ceil(realImageableY); retValue.width = (int)Math.floor(realImageableWidth); retValue.height = (int)Math.floor(realImageableHeight); return retValue; } /** * Affiche du texte indépendamment de la résolution * * @param graphics le contexte graphics * @param text le texte à afficher * @param x l'abscisse où placer le texte * @param y l'ordonnée où placer le texte */ public void printText(Graphics2D graphics, String text, int x, int y){ Font currentFont = graphics.getFont(); // Calcul de l'échelle du texte double fontScale = ((double)(resolution*currentFont.getSize()))/(JAVA_DPI * 72.0); // Transformation de la police AffineTransform fontShapeTransform = new AffineTransform(); fontShapeTransform.setToScale(fontScale, fontScale); // Font de récupération de glyph vector Font computeFont = new Font(currentFont.getName(), currentFont.getStyle(), 72); // Font de calcul de largeur d'un caractères => ignore le flag italique Font sizeFont = computeFont; if (font.isItalic()) { sizeFont = new Font(currentFont.getFontName(), currentFont.getStyle()-Font.ITALIC, 72);

Page 27: Java Print

L'impression en java par Bruno Richeton

- 27 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

} // La position courante du texte Point2D.Double textPos = new Point2D.Double(x, y); // Récupère le contexte de rendu de police FontRenderContext frc = graphics.getFontRenderContext(); // On boucle sur chaque caractères char[] carIterator = new char[1]; int textLength = text.length(); for (int i = 0; i < textLength; i++) { // récupère le caractère courant text.getChars(i, i+1, carIterator, 0); // Récupère le glyph de ce caractère pour la police de calcul GlyphVector glyph = computeFont.createGlyphVector(frc, carIterator); graphics.translate(textPos.x, textPos.y); glyph.setGlyphTransform(0, fontShapeTransform); graphics.drawGlyphVector(glyph, 0.f, 0.f); graphics.translate(-textPos.x, -textPos.y); // Incrémente la position TextLayout layout = new TextLayout(new String(carIterator), sizeFont, frc); textPos.x += (double)layout.getAdvance()* fontScale; } } /** * Affiche du texte indépendamment de la résolution * * @param graphics le contexte graphics * @param text le texte à afficher * @param x l'abscisse où placer le texte * @param y l'ordonnée où placer le texte */ public void printPreviewText(Graphics2D graphics, String text, int x, int y, int printDpi, double screenScale){ Font currentFont = graphics.getFont(); int screenDpi = Toolkit.getDefaultToolkit().getScreenResolution(); // Calcul l'échelle du texte double fontScale = ((double)(printDpi*currentFont.getSize()))/(double)(screenDpi * 72.0); // Transformation de la police AffineTransform fontShapeTransform = new AffineTransform(); fontShapeTransform.setToScale(fontScale*screenScale, fontScale*screenScale); // Font de récupération de glyph vector Font computeFont = new Font(currentFont.getName(), currentFont.getStyle(), 72); // Font de calcul de largeur d'un caractères => ignore le flag italique Font sizeFont = computeFont; if (font.isItalic()) { sizeFont = new Font(currentFont.getFontName(), currentFont.getStyle()-Font.ITALIC, 72); } graphics.scale(1.0/screenScale,1.0/screenScale); // La position courante du texte Point2D.Double textPos = new Point2D.Double(x*screenScale, y*screenScale); // Récupère le contexte de rendu de police FontRenderContext frc = graphics.getFontRenderContext(); // On boucle sur chaque caractère char[] carIterator = new char[1]; int textLength = text.length(); for (int i = 0; i < textLength; i++) { // Récupère le caractère courant

Page 28: Java Print

L'impression en java par Bruno Richeton

- 28 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

text.getChars(i, i+1, carIterator, 0); // Récupère le glyph de ce caractère pour la police de calcul GlyphVector glyph = computeFont.createGlyphVector(frc, carIterator); glyph.setGlyphTransform(0, fontShapeTransform); graphics.translate(textPos.x, textPos.y); graphics.drawGlyphVector(glyph, 0.f, 0.f); graphics.translate(-textPos.x, -textPos.y); // Incrémente la position TextLayout layout = new TextLayout(new String(carIterator), sizeFont, frc); textPos.x += (double)layout.getAdvance()* fontScale*screenScale; } graphics.scale(screenScale,screenScale); } public void preview(Graphics2D graphics, Dimension screenSize, PrintParameters printParameters){ // Taille de la feuille en pouce Rectangle2D.Double paperArea = printParameters.getPaperArea(); // Convertit en pixels int printDpi = printParameters.getDPI(); double paperAreaPixelsWidth = paperArea.width * printDpi; double paperAreaPixelsHeight = paperArea.height * printDpi; double xScaleRatio = screenSize.width / paperAreaPixelsWidth; double yScaleRatio = screenSize.height / paperAreaPixelsHeight; double projectionScale = xScaleRatio; if (yScaleRatio < xScaleRatio){ projectionScale = yScaleRatio; } int screenPageWidth = (int)(paperAreaPixelsWidth * projectionScale); int screenPageHeight = (int)(paperAreaPixelsHeight * projectionScale); int xScreenPageOffset = (screenSize.width - screenPageWidth)/2; int yScreenPageOffset = (screenSize.height - screenPageHeight)/2; // Dessine le fond du composant graphics.setColor(Color.DARK_GRAY); graphics.fillRect(0, 0, screenSize.width, screenSize.height); // Modifie la transformation graphics.translate(xScreenPageOffset, yScreenPageOffset); graphics.scale(projectionScale, projectionScale); // Dessine la feuille graphics.setColor(Color.WHITE); graphics.fillRect(0, 0, (int)paperAreaPixelsWidth, (int)paperAreaPixelsHeight); // Dessine les marges Rectangle2D.Double margin = printParameters.getPrintableArea(); int xMargin = (int)(margin.x * printDpi); int yMargin = (int)(margin.y * printDpi); int widthMargin = (int)(margin.width * printDpi); int heightMargin = (int)(margin.height * printDpi); graphics.setColor(Color.LIGHT_GRAY); graphics.drawRect(xMargin, yMargin, widthMargin, heightMargin); // Définit le clipping graphics.setClip(xMargin, yMargin, widthMargin, heightMargin);

Page 29: Java Print

L'impression en java par Bruno Richeton

- 29 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

graphics.setColor(Color.BLACK); graphics.drawOval(xMargin, yMargin, widthMargin, heightMargin); Rectangle marg = new Rectangle(xMargin, yMargin, widthMargin, heightMargin); graphics.setFont(font); printPreviewText(graphics, marg.toString(), xMargin, yMargin+heightMargin/2, printDpi, projectionScale); } public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { // Par défaut, retourne NO_SUCH_PAGE => la page n'existe pas int retValue = Printable.NO_SUCH_PAGE; if (pageIndex==0){ Graphics2D g2d = (Graphics2D)graphics; // Restaure la résolution réelle Rectangle margin = restoreRealDpi(g2d, pageFormat);

// Dessine le rectangle graphics.setColor(Color.BLACK); graphics.drawOval(0, 0, margin.width, margin.height); // Affiche les marges graphics.setFont(font); printText(g2d, margin.toString(), 0, margin.height/2);

// La page est valide retValue = Printable.PAGE_EXISTS; } return retValue; }}

import java.awt.BorderLayout;import java.awt.Dimension;import java.awt.FlowLayout;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.ComponentEvent;import java.awt.event.ComponentListener;import java.awt.print.PrinterException;import java.awt.print.PrinterJob;import javax.print.attribute.PrintRequestAttributeSet;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JPanel;import sun.print.DialogTypeSelection;

public class Main8 extends JFrame{ private PrintParameters printParameters = new PrintParameters(); private PrintRectangleResTextPreview printableObject = new PrintRectangleResTextPreview(); private PreviewPanel previewPanel = new PreviewPanel(); /** Constructeur par défaut de Main8 */ public Main8() { JPanel internalPanel = new JPanel(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); internalPanel.setLayout(new BorderLayout()); setTitle(printParameters.getPrintService().getName());

Page 30: Java Print

L'impression en java par Bruno Richeton

- 30 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER)); JButton configButton = new JButton("Configuration"); configButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { PrinterJob job = PrinterJob.getPrinterJob(); PrintRequestAttributeSet attribSet = printParameters.getAttributes(); attribSet.add(DialogTypeSelection.NATIVE); try { job.setPrintService(printParameters.getPrintService()); if (job.printDialog(attribSet)){ printParameters = new PrintParameters(attribSet, job.getPrintService()); setTitle(printParameters.getPrintService().getName()); previewPanel.repaint(); } } catch (PrinterException ex) { ex.printStackTrace(); } } }); buttonPanel.add(configButton); JButton printButton = new JButton("Imprimer"); printButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { PrinterJob job = PrinterJob.getPrinterJob(); job.setPrintable(printableObject); job.setPrintService(printParameters.getPrintService()); job.print(printParameters.getAttributes()); } catch (PrinterException ex) { ex.printStackTrace(); } } }); buttonPanel.add(printButton); internalPanel.add(buttonPanel, BorderLayout.SOUTH); internalPanel.add(previewPanel, BorderLayout.CENTER); setContentPane(internalPanel); pack(); } /** * @param args the command line arguments */ public static void main(String[] args) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new Main8().setVisible(true); } }); } private class PreviewPanel extends JPanel{ PreviewPanel(){ addComponentListener(new ComponentListener() { public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentResized(ComponentEvent e) { repaint(); } public void componentShown(ComponentEvent e) { repaint();

Page 31: Java Print

L'impression en java par Bruno Richeton

- 31 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

} }); setPreferredSize(new Dimension(400,400)); } public void paint(Graphics g) { Graphics2D g2d = (Graphics2D)g; Dimension size = getSize(); printableObject.preview(g2d, size, printParameters); } }}

Dans un premier temps, nous enrichissons la précédente implémentation de Printable dans une nouvelle classePrintRectangleResTextPreview. Deux méthodes ont été rajoutées :

• La méthode printPreviewText.• La méthode preview

La méthode printPreviewText reprend la logique de la précédente méthode printText en rajoutant la prise en comptedu scaling associé au contexte graphique. A savoir que dans tout le corps de cette méthode, la mise à l'échelle estresetée (graphics.scale(1.0/screenScale,1.0/screenScale);) car l'object TextLayout, utilisé pour calculer l'avance dutexte, prend en compte cette mise à l'échelle mais de manière discontinue (En interne, les tailles de polices doiventêtre arrondies au point près). En annulant cette transformation, nous prenons entièrement la main sur la taille de lapolice souhaitée et sur son avance.

La méthode preview détermine la mise à l'échelle nécessaire pour effectuer le preview et modifie la transformationdu contexte graphique pour émuler le bon affichage. Elle s'occupe aussi d'afficher la page blanche ainsi que lesmarges d'impression.

Pour tester l'aperçu avant impression, une petite application basée sur une JFrame a été créée. Le comportementde cette application repose sur :

• Une instance printParameters de PrintParameters qui contiennent la configuration d'impression actuellementsélectionnée. Ces paramètres sont remis à jour lors d'un changement de configuration et utilisé pourl'impression (Code des boutons "Configurer" et "Imprimer").

• Une instance printableObject de la classe PrintRectangleResTextPreview pour rendre l'aperçu et l'impression.• Un composant interne previewPanel qui surcharge sa méthode paint pour afficher l'aperçu.

V-C-4 - L'impression par bande

Une impression en A4 et 600 DPI revient à générer une image d'environ 5000X7000 pixels. La mémoire de laJVM peut donc très vite arriver à saturation si l'image imprimée est rendue en une seule passe. Heureusement, unmécanisme très simple existe : faire le rendu successif de petits bouts de l'image finale comme une mosaïque. Ilsemble que Java gère d'office ce mode car j'ai constaté que la méthode print d'un Printable peut être appelée plusieursfois de suite pour une même page. Par contre, j'ai constaté que certains attributs de dessin pouvaient modifier cecomportement comme afficher une couleur avec transparence. Dans ce cas là, Java essaye d'allouer une image à lataille correspondante mais en 72 DPI qui est le paramétrage initial du contexte de rendu transmis à la méthode print.Dans ce cas là, tout est faussé car le nouveau rendu essaye toujours de faire un rendu à la résolution de l'imprimanteet seul le coin supérieur gauche est imprimé comme le montre le schéma ci-dessous :

Page 32: Java Print

L'impression en java par Bruno Richeton

- 32 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

Il y a donc certains comportements à éviter lors de l'impression comme utiliser la transparence. Je n'ai pas la listeexhaustive de ces comportements bannis mais leur utilisation se remarque à la première impression.

V-C-5 - Affichage de la progression

Afficher une boîte pendant l'impression se résout en utilisant plusieurs threads :

Dans le diagramme ci-dessus :

• Les tâches identifiées par un nombre sont exécutées dans le thread AWT.• Les tâches identifiées par une lettre sont exécutées dans un thread spécifique à l'impression.

Modifions donc la JFrame de l'exemple précédent, pour respecter ce schéma :

import java.awt.BorderLayout;

Page 33: Java Print

L'impression en java par Bruno Richeton

- 33 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

import java.awt.Dimension;import java.awt.FlowLayout;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Point;import java.awt.Toolkit;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.ComponentEvent;import java.awt.event.ComponentListener;import java.awt.print.PrinterException;import java.awt.print.PrinterJob;import javax.print.attribute.PrintRequestAttributeSet;import javax.swing.JButton;import javax.swing.JDialog;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JPanel;import javax.swing.SwingUtilities;import sun.print.DialogTypeSelection;

/** * * */public class Main9 extends JFrame{ private PrintParameters printParameters = new PrintParameters(); private PrintRectangleResTextPreview printableObject = new PrintRectangleResTextPreview(); private PreviewPanel previewPanel = new PreviewPanel(); private PrinterThread printerThread = null; private ProgressDialog dialog = null; /** Constructeur par défaut de Main8 */ public Main9() { JPanel internalPanel = new JPanel(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); internalPanel.setLayout(new BorderLayout()); setTitle(printParameters.getPrintService().getName()); JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER)); JButton configButton = new JButton("Configuration"); configButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { PrinterJob job = PrinterJob.getPrinterJob(); PrintRequestAttributeSet attribSet = printParameters.getAttributes(); attribSet.add(DialogTypeSelection.NATIVE); try { job.setPrintService(printParameters.getPrintService()); if (job.printDialog(attribSet)){ printParameters = new PrintParameters(attribSet, job.getPrintService()); setTitle(printParameters.getPrintService().getName()); previewPanel.repaint(); } } catch (PrinterException ex) { ex.printStackTrace(); } } }); buttonPanel.add(configButton); JButton printButton = new JButton("Imprimer"); printButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (printerThread == null){

Page 34: Java Print

L'impression en java par Bruno Richeton

- 34 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

printerThread = new PrinterThread(); dialog = new ProgressDialog(); printerThread.start(); dialog.setVisible(true); } } }); buttonPanel.add(printButton); internalPanel.add(buttonPanel, BorderLayout.SOUTH); internalPanel.add(previewPanel, BorderLayout.CENTER); setContentPane(internalPanel); pack(); } /** * @param args the command line arguments */ public static void main(String[] args) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new Main9().setVisible(true); } }); } /** * Panel de l'aperçu **/ private class PreviewPanel extends JPanel{ PreviewPanel(){ addComponentListener(new ComponentListener() { public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentResized(ComponentEvent e) { repaint(); } public void componentShown(ComponentEvent e) { repaint(); } }); setPreferredSize(new Dimension(400,400)); } /** */ public void paint(Graphics g) { Graphics2D g2d = (Graphics2D)g; Dimension size = getSize(); printableObject.preview(g2d, size, printParameters); } } /** * Boîte de progression **/ private class ProgressDialog extends JDialog { JLabel progressLabel = new JLabel(); ProgressDialog(){ super(Main9.this, true); setTitle("Impression"); setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);

Page 35: Java Print

L'impression en java par Bruno Richeton

- 35 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

JPanel panel = new JPanel(); panel.setPreferredSize(new Dimension(300, 40)); panel.setLayout(new BorderLayout()); panel.add(progressLabel, BorderLayout.CENTER); setContentPane(panel); pack(); // Centre la boîte Dimension dialogSize = getSize(); Dimension parentSize = Main9.this.getSize(); Point parentLocation = Main9.this.getLocation(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int xBox = parentLocation.x+(parentSize.width - dialogSize.width)/2; int yBox = parentLocation.y+(parentSize.height - dialogSize.height)/2; if (xBox < 0){ xBox = 0; } else { if ((xBox +dialogSize.width)> screenSize.width){ xBox = screenSize.width-dialogSize.width; } } if (yBox < 0){ yBox = 0; } else { if ((yBox +dialogSize.height)> screenSize.height){ yBox = screenSize.height-dialogSize.height; } } setLocation(xBox, yBox); } public void close(){ if (SwingUtilities.isEventDispatchThread()){ dispose(); dialog = null; } else { SwingUtilities.invokeLater(new Runnable() { public void run() { close(); } }); } } public void setText(String text){ if (SwingUtilities.isEventDispatchThread()){ progressLabel.setText(text); } else { SwingUtilities.invokeLater(new TextThread(text)); } } } private class TextThread implements Runnable{ String text; TextThread(String text){ this.text = text; }

public void run() { dialog.setText(text); } }

Page 36: Java Print

L'impression en java par Bruno Richeton

- 36 -Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentationconstitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2009 Bruno RICHETON. Aucune reproduction, même partielle, nepeut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vousencourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Cette page est déposée à la SACD.

http://bruno-richeton.developpez.com/tutoriels/java/swing/impression-java/

/** * Thread d'impression **/ private class PrinterThread extends Thread{ PrinterThread(){ super("Thread d'impression"); } public void run() { try { dialog.setText("Début de l'impression"); sleep(1000); PrinterJob job = PrinterJob.getPrinterJob(); job.setPrintable(printableObject); job.setPrintService(printParameters.getPrintService()); dialog.setText("Impression"); sleep(1000); job.print(printParameters.getAttributes()); dialog.setText("Fin de l'impression"); sleep(1000); dialog.close(); } catch (Throwable ex) { ex.printStackTrace(); } printerThread = null; } }}

Deux champs ont été rajoutés à la classe :

• Une instance du thread d'impression PrinterThread.• Une instance de la boîte de progression ProgressDialog

V-C-5-a - Le thread d'impression

Le thread d'impression réalise l'impression à proprement parler. Par contre, il a accès à la boîte de dialogue pourlui envoyer des notifications de progression. Dans un vrai projet, ces notifications seraient plutôt envoyées parl'implémentation de Printable pour afficher des informations sur la page en cours d'impression. Par contre, à la fin del'impression, le thread a la charge de détruire la boîte de progression par l'appel à dialog.close().

V-C-6 - La boîte d'impression

Dans cet exemple, la boîte contient juste un label pour afficher un texte. Cette boîte propose la méthode setText pourmodifier le contenu de ce label. Cette méthode doit obligatoirement être synchronisée avec le thread AWT puisqu'elleest destinée à être appelée par le thread d'impression. Dans le même acabit, la boîte propose la méthode close()pour pouvoir être fermée à la fin de l'impression