© 1999-2003, Flemming Koch Jensen
Alle rettigheder forbeholdt
Udskrift til printer
Jeg skal med det samme indrømme, at jeg ikke har studeret mulighederne i Java til bunds når det gælder udskrift til printer. Det skyldes at det fremstår så ringe, at det umuligt kan være den endelige løsning som Sun har tænkt sig at byde Java-verden. Grafik kan kun udskrives i en opløsning hvor en matrix-printer kan være med og al udskift tager ufattelig lang tid. Skulle jeg gøre skarn uret, og der findes enkle løsninger på nogle af de problemer jeg skitserer nedenfor, hører jeg gerne om det - det er trods alt rart at kunne udskrive fra sine programmer.

 

 

Svag side af Java Mange applikationer har behov for at kunne udskrive til en printer. Java giver også mulighed for dette, men man skal på forhånd nedjustere forventningerne - det er en meget besværlig og ineffektiv løsning man har valgt.
 

 

1. Forløbet af et printerjob

PrinterJob Generelt kalder man en operation, hvorved der udskrives en samlet række af sider på en printer, for et printerjob. Når man ønsker at udskrive, skal man først have et PrinterJob-objekt, der skal repræsenterer selve operationen (udskrivningen).
fabriks-metode Man får et sådant objekt ved at kalde en statisk fabriksmetode på PrinterJob-klassen. Dette gøres i første linie af følgende try-blok:
import java.awt.print.*;

try {
  PrinterJob job = PrinterJob.getPrinterJob();
  job.setPrintable( ... );
  
  if ( job.printDialog() )
    job.print();
}
catch ( PrinterException e ) {
  ...
}
Objekt leverer siderne til udskriften set-metoden der kaldes i den efterfølgende linie angiver hvilket objekt, der repræsenterer det der skal udskrives. Objektet skal have Printable-interfacet, som anvendes af PrinterJob-objektet under udskrivningen. Vi vil senere se hvordan man kan lave Printable objekter.
  Efter printerjobbet har fået besked på hvilket objekt der skal levere siderne til udskriften, skal brugeren involveres. Ved kaldet af printDialog bliver den platformsafhængige udskriftsdialog vist. På min PC kunne det f.eks. se ud som følger:
Figur 1:
Udskriftsdialog
Returnerer boolsk Som if-sætningen indikerer returnerer metoden boolsk, om brugeren vælger OK eller ej. Grunden til at det side-interval der tilbydes går fra 1 til 9999, skyldes at PrinterJob'et ikke ved hvor mange sider der er, før den har udskrevet dem alle.
  Hvis brugeren vælger OK, kalder vi print-metoden på selve PrinterJob'et - printeren begynder at summe, og en udskrift er på vej.
  Hvis noget går galt under vejs kastes der en PrinterException, som man kan håndtere på passende vis.
  Ovenstående eksempel kan generelt anvendes i forbindelse med enhver udskrift man måtte ønske. Det der adskiller den ene udskrift fra den anden er objektet der leverer siderne der udskrives.
 

 

2. Printable-interfacet

  Objektet der leverer siderne til udskrift skal som nævnt have Printable-interfacet. Dette interface indeholder kun én metode:
int print( Graphics g, PageFormat format, int pageNo )
  Formålet med denne metode, er at PrinterJob-objektet kalder denne metode med forskellige angivelser af pageNo efterhånden som siderne skal udskrives. pageNo er det ønskede sidenummer (starter med 0), og g er den Graphics man skal "tegne" siden med.
  At man skal tegne siden med en Graphics gør det hele lidt low-level, men det giver til gengæld også god mulighed for at lave lige hvad man vil.
  Metoden returnerer en integer, der skal være en af følgende to statik konstanter, som er erklæret i Printable-interfacet:
NO_SUCH_PAGE
PAGE_EXISTS
  Ved at returnere en af disse værdier, indikerer metoden om den pågældende side eksisterer.
   
Eksempel Lad os se et eksempel på en simpel implementation af print-metoden:
public int print( Graphics g, PageFormat format, int pageNo ) {
  if ( pageNo >= 3 )
    return NO_SUCH_PAGE;
  
  Paper paper = format.getPaper();
  int xStart = (int) paper.getImageableX();
  int yStart = (int) paper.getImageableY();
  int width  = (int) paper.getImageableWidth();
  int height = (int) paper.getImageableHeight();
  
  int xCenter = xStart + width/2;
  int yCenter = yStart + height/2;
  
  g.setColor( Color.black );
  g.drawString( "Dette er side " + (pageNo+1), xCenter, yCenter );
  
  return PAGE_EXISTS;
}
  Man ser af if-sætningen, at vi kun har siderne 0, 1 og 2; hvilket fra brugerens perspektiv er side 1, 2 og 3, som det ses i drawString-kaldet nederst. Bemærk forøvrigt at teksten ikke centreres helt, da der ikke tages højde for tekstens bredde (for at holde eksemplet simpelt).
  Ind imellem har vi en større historie, der drejer sig om sidens dimensioner. Vi kan hente oplysninger om hvor meget plads vi har at gøre med, fra PageFormat-parameteren. Vi gør dette ved først at få Paper-objektet, der indeholder oplysninger om selve skrivefladen. Som man ser arbejder vi med et offset i forhold til sidens reelle ydergrænser, der er ganske enkelt en margin.
  Metoderne:
double getImageableX()
double getImageableY()
double getImageableWidth()
double getImageableHeight()
  giver os disse oplysninger. De første to giver offset for øverste venstre hjørne af siden, mens de to sidste giver selve størrelsen af skriveområdet. Bemærk at disse alle returnerer en double hvilket udelukkende er til besvær! Værdierne er nemlig er pixels, og disse anvendes normalt altid som integers. Man ser da også at de konsekvent castes til integer i eksemplet ovenfor.
 

 

3. Design problemer

  Som antydet tidligere er Java's måde at håndtere udskriving til en printer ikke uden problemer - det er faktisk temlig besværligt at arbejde med.
 

 

3.1 Vilkårlig side

Side 83 - tak Det grundlæggende problem er at Printable-interfacet kræver, at man kan levere en vilkårlig side på kommando! Forestil dig, at du er ved at udskrive et tekstdokument med figurer og andet godt - ud af det blå modtager man et kald af print, der kræver, at man med det medfølgende Graphics tegner side 83 - hvad gør man så?
  Problemet er ikke, at det ikke kan lade sig gøre, men at det er meget krævende. Hvor starter side 83? Hvad er det første ord der skal stå øverst i venstre hjørne? Hvor langt skal vi ind i tekstdokumentet før vi når side 83?
  Den mest "enkle" måde at besvare dette spørgsmål er at tegne side 1-82, og se hvor langt vi så er kommet - ufattelig ineffektivt! Normalt kan man dog nøjes med at foretage de beregner der indgår i at opsætte side 1-82, og undlade selve tegneoperationerne, men det er stadig meget usmart.
 

 

3.2 Udokumenteret optimering

  Lad os indføre en udskrift til skærmen i starten af print-metoden i eksmeplet ovenfor:
public int print( Graphics g, PageFormat format, int pageNo ) {
  System.out.println( pageNo );
  
  if ( pageNo >= 3 )
    return NO_SUCH_PAGE;
  
  ...
}
  Ved en kørsel af programmet fås følgende udskrift:
0
0
1
1
2
2
3
  Som man ser er der system i tingene. Siderne kommer i kronologisk rækkefølge, men det er lidt uklart hvorfor alle sider, skal laves to gange (side 3 optræder naturligvis kun én gang, da den ikke findes).
  Ovenstående sammenhæng er udokumenteret, dvs. man kan ikke reelt regne med at kaldene i forbindelsem ed udskrift altid vil forløbe på denne måde. Vælger man dog at antage det, kan man udnytte de fortløbende sidenumre til at generere siderne inkrementerende. Man gør det ved, efter at have genereret den enkelte side, at huske hvor den sluttede - så ved man hvor man skal starte når der bedes om den næste side. Denne løsning kan også laves så den samtidig er robust overfor vilkårlige sider, da man blot kan teste for om siderne forespørges fortløbende, og i modsat fald generere dem fra gang til gang.
 

 

3.3 Ringe opløsning

Det er min erfaring at grafik - dvs. Image's - kun kan udskrives i 72 dpi; hvilket er helt utilfredsstillende. Det er muligt at jeg har overset at man kan ændre opløsningen, og jeg hører gerne om det hvis man har fundet en løsning.
 

 

3.4 Gab

Udskrift til en printer i Java er meget langsomt. Jeg har ikke prøvet det, men jeg vil udmiddelbart forvente at et tekstdokument som beskrvet ovenfor på ca. 80 sider nemt vil kunne tage flere timer at udskrive. Det gør at man nok må regne det som urealistisk at bruge udskrift til en printer i Java til noget fornuftigt; hvis det fylder mere end nogle få sider.