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écouvert qu'un attribut était ajouté dans les requêtes et qui visiblement pilote la boîte affichée. C'est l'attribut de catégorie sun.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 release
Au 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 à la méthode print d'un printable est automatiquement configuré en 72 DPI. Toutefois, en interrogeant la configuration de ce contexte (getDeviceConfiguration), il est possible d'obtenir la vraie résolution. Comment passer de la configuration 72 DPI à la configuration résolution réelle ?
Tout est dans la transformation associée au contexte graphique. Par défaut, elle est donc configurée pour une sortie en 72 DPI en respectant les marges d'impression ainsi que l'orientation de la feuille. Le développeur doit donc tout reconfigurer :
- Associer au contexte graphique une transformation identité pour passer en mode 1 unité graphique = 1 pixels.
- 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;
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);
// 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 contexte graphique 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. Plus la résolution est élevée, plus le texte est petit alors que dans le code sa taille est constante (16 points). Ce fonctionnement est tout à fait normal pour Java puisque celui-ci considère toujours que la résolution est de 72 DPI pour 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 ensuite comme 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
(
);
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
*
@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
;
}
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 a une 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 taille fontSize à 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'on positionne 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 italique qui 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 composant ne pose donc aucun souci. Il suffit d'appeler la méthode getSize(). Pour la résolution écran, elle est tout simplement retourné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 avons vu qu'il est possible de récupérer les choix de l'utilisateur après une configuration de l'imprimante grâce aux requêtes d'impression. Il nous suffit d'encapsuler les paramètres importants à nos yeux dans une instance d'une classe PrintParameters que nous mettrons à jour après chaque modification de la configuration d'impression.
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
];
// 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.0
f;
physicalMargin[1
] =
0.0
f;
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);
}
}
/**
* 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. Elle possè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 :
- 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 set de requêtes. Tous les attributs répondant sur le même principe, nous décrivons seulement la récupération de la ré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"
);
}
}
}
}
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
(
)){
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
);
}
// 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
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);
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
(
));
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
(
);
}
}
);
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 classe PrintRectangleResTextPreview. 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 compte du scaling associé au contexte graphique. A savoir que dans tout le corps de cette méthode, la mise à l'échelle est resetée (graphics.scale(1.0/screenScale,1.0/screenScale);) car l'object TextLayout, utilisé pour calculer l'avance du texte, 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 la police 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 transformation du contexte graphique pour émuler le bon affichage. Elle s'occupe aussi d'afficher la page blanche ainsi que les marges d'impression.
Pour tester l'aperçu avant impression, une petite application basée sur une JFrame a été créée. Le comportement de cette application repose sur :
- Une instance printParameters de PrintParameters qui contiennent la configuration d'impression actuellement sélectionnée. Ces paramètres sont remis à jour lors d'un changement de configuration et utilisé pour l'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 la JVM peut donc très vite arriver à saturation si l'image imprimée est rendue en une seule passe. Heureusement, un mécanisme très simple existe : faire le rendu successif de petits bouts de l'image finale comme une mosaïque. Il semble que Java gère d'office ce mode car j'ai constaté que la méthode print d'un Printable peut être appelée plusieurs fois de suite pour une même page. Par contre, j'ai constaté que certains attributs de dessin pouvaient modifier ce comportement comme afficher une couleur avec transparence. Dans ce cas là, Java essaye d'allouer une image à la taille 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'imprimante et seul le coin supérieur gauche est imprimé comme le montre le schéma ci-dessous :
Il y a donc certains comportements à éviter lors de l'impression comme utiliser la transparence. Je n'ai pas la liste exhaustive 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;
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
){
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);
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);
}
}
/**
* 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 pour lui envoyer des notifications de progression. Dans un vrai projet, ces notifications seraient plutôt envoyées par l'implémentation de Printable pour afficher des informations sur la page en cours d'impression. Par contre, à la fin de l'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 pour modifier le contenu de ce label. Cette méthode doit obligatoirement être synchronisée avec le thread AWT puisqu'elle est 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