Travaillez en millimètres pour imprimer sur n'importe quelle imprimante
Suite à question posée sur le forum nzn.fr.delphi, j'ai décidé de traduire ce petit article sur les calculs de position en millimètres sur le canvas des imprimantes.
Il faut savoir que le canvas de l'objet TPrinter n'accepte (comme tous les TCanvas) que des coordonnées en pixels. L'être humain n'étant pas habitué à ce
genre de mesure, il est difficile pour lui de se repérer dans une telle dimension. Le but de la manoeuvre est donc d'utiliser quelques petits calculs simples
pour se repérer dans un système plus conventionnel (au terme humain de la chose).
Pour réaliser cela, il va nous falloir des éléments de base. On a alors 2 solutions.
L'objet Printer
L'objet Printer est automatiquement créé par Delphi dès que le code le réclame. Il est nécessaire d'inclure dans les Uses l'unité Printers. Il n'est
donc nul besoin de le créer explicitement. On pourra faire par exemple:
LargeurCanvas := Printer.PageWidth; {ce qui nous donnera la largeur imprimable de la page pour l'imprimante en cours}
Le seul problème est que, pour obtenir un résultat valable, il faut obligatoirement lancer une impression avec la procédure BeginDoc. Cela implique
donc le démarrage d'une impression, l'obtention des valeurs, puis l'abandon de la procédure. Vous me direz que si on veut imprimer, on est bien
obligé d'en passer par là. Mais on peut avoir besoin de connaître les dimensions de la page imprimable sans pour autant lancer une impression.
Un prochain article sur "Comment réaliser un aperçu avant impression" montrera pourquoi. La procédure normale serait donc :
Printer.BeginDoc;
LargeurCanvas := Printer.PageWidth;
Printer.Abort;
L'autre solution consiste à utiliser l'API GetDeviceCaps qui nous fournit tous les renseignements nécessaires sans lancer d'impression.
L'API GetDeviceCaps
Cette fonction de windows nécessite un handle et une constante pour indiquer quel type de résultat on veut obtenir. Pour l'objet TPrinter, le handle
sera Printer.Canvas.handle. Dans notre cas, nous utiliserains les constantes LOGPIXELSX et LOGPIXELSY qui permettent à GetDeviceCaps de
renvoyer respectivement le nombre de pixels par pouce logique. On aura noté qu'il existe une constante pour les valeurs en X et une pour les
valeurs en Y. En ce qui concerne l'écran, les 2 valeurs seront différentes, car physiquement, un pixel est plus haut que large. Sur une imprimante,
les 2 valeurs seront (généralement) identiques, car dans ce cas, un pixel représente un point parfaitement rond. Aussi, l'une ou l'autre constante
renverront le même résultat.
Pour obtenir le nombre de pixels par pouce pour l'imprimante, nous écrirons donc :
XPixelsParPouce := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
YPixelsParPouce := GetDeviceCaps(Printer.Handle, LOGPIXELSY);
Ainsi, pour commencer l'impression d'un texte à 1 pouce du bord gauche de la feuille, et à 1 pouce du haut de la feuille, on écrira :
Printer.Canvas.TextOut(XPixelsParPouce, YPixelsParPouce, 'Mon texte');
Maintenant, si nous voulons travailler en millimètres pour plus de commodités, nous devons transformer nos coordonnées en millimètres. Comme
vous le savez certainement, 1 pouce = 25.4 mm, donc 1 mm = 1 / 25.4 pouce. Nous pouvons donc calculer directement :
XMillimetresParPouce := MulDiv(GetDeviceCaps(Printer.Handle, LOGPIXELSX), 10, 254);
YMillimetresParPouce := MulDiv(GetDeviceCaps(Printer.Handle, LOGPIXELSY), 10, 254);
L'utilisation de MulDiv évite les résultats avec virgules pour qu'elle renvoie un nombre entier. Ici, plutôt que de stocker cette valeur dans une valeur en début
de code, nous allons les calculer 'à la volée' à chaque fois que cela sera nécessaire. Pourquoi faire cela ? Et bien, prenons par exemple un nombre de 360 pixels
par pouce. Le résultat de la conversion en virgule flottante donne 14.17. Ce qui veut dire que lors de l'impression, avec une valeur arrondie à 14, on perdrait 1 pixel
tous les 6 millimètres. Ca parait peu à première vue, mais pourrait provoquer de drôles de choses à la sortie. Nous conserverons donc cette valeur sous forme
de renvoi de fonction.
function Millimetres2PixelsX(Millims: integer): integer;
begin
result := MulDiv(GetDeviceCaps(Printer.Handle, LOGPIXELSX), 10 * Millims, 254);
end;
function Millimetres2PixelsY(Millims: integer): integer;
begin
result := MulDiv(GetDeviceCaps(Printer.Handle, LOGPIXELSY), 10 * Millims, 254);
end;
Ainsi, pour imprimer un texte à 50 millimètres du bord gauche de la feuille et 120 millimètres du bord haut, on écrira :
Printer.Canvas.TextOut( Millimetres2PixelsX(50), Millimetres2PixelsY(120), 'Mon Texte à (50, 120)');
ATTENTION: Ce code imprimera le texte avec un léger décalage supplémentaire. Il s'agit de la zone non imprimable définie par l'imprimante elle-même.
Pour connaitre la taille de cette zone non imprimable, vous utiliserez les constantes PHYSICALOFFSETX et PHYSICALOFFSETY. Vous obtiendrez alors les
valeurs en pixels que vous transformerez en millimètres de la même manière que précédemment.
function NonImprimableHorizMM: integer; {arrondi}
begin
result := MulDiv(GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX), 10, 254);
end;
Nous pouvons alors terminer ainsi pour imprimer notre texte à exactement 50 mm du bord gauche :
Printer.Canvas.TextOut(Millimetres2PixelsX(50 - NomImprimableHorizMM), Millimetres2PixelsY(120), 'Mon Texte à (50, 120)');
Le même calcul se faite sur la hauteur.
Voilà ! Vous avez les éléments de base pour imprimer ou voulez, dans un repère connu.
Partager