Home » Tutorials » Object Pascal/RTL » Drucken

Drucken

Über mehrere Seiten drucken

Stehen wir vor der Aufgabe, ein größeres Schaubild, Diagramm o.ä. ausdrucken zu müssen, so müssen wir uns an die vorhin erwähnten API-Funktionen erinnern. Da ging es um Window und Viewport – und deren Ursprungskoordinaten lassen sich auch verschieben. Doch wie haben wir uns das vorzustellen?

Der große grüne Kasten in der Zeichnung stellt unsere virtuelle Leinwand dar, das Window. Die kleineren grauen Kästchen stehen für je ein DIN A 4-Blatt, also die Ausgabefläche, den Viewport. Würden wir unseren Ausdruck wie bisher an den Drucker schicken, würde dieser lediglich Seite 1 ausdrucken. Alles andere geht über den Papierrand hinaus und wird deshalb verschluckt. Denn standardmäßig steht der Koordinatenursprung (hier als dunkelgrünes Koordinatensystem in der linken oberen Ecke von Seite 3 eingezeichnet) an der linken oberen Ecke des Window, die identisch ist mit der linken oberen Ecke des Viewports.
Mit Hilfe der Win32-API-Funktion SetWindowOrgEx lässt sich der Koordinatenursprung auf unserer Leinwand verschieben. Verschieben wir ihn z.B. um zwei Seitenbreiten nach rechts, wird alles gedruckt, was der Fläche eines Viewports (sprich eines Blattes) entspricht und an dieser Stelle beginnt. Die Koordinaten unserer Leinwand müssen dafür nicht umgerechnet werden. Selbst wenn wir hier beispielsweise bei einem x-Wert von 500 mm sind, wird das automatisch auf die Viewport-Koordinaten umgerechnet.
Nun ist das Drucken über mehrere Seiten kein Hexenwerk mehr: Wir stellen uns den Ausdruck wie in obiger Abbildung als Tabelle vor und legen einfach zwei Schleifen über unsere Leinwand – eine über die „Zeilen“, die jeweils einer Papierhöhe entsprechen, und innerhalb der Zeilen nochmal eine über die „Spalten“, also die Papierbreite. Entsprechend verschieben wir den Koordinatenursprung unseres Window. Hier das Gerüst dazu:

procedure DruckenUeberMehrereSeiten;
var seitenbreite, seitenhoehe, offsetX, offsetY: integer;
    mmpropixelx, mmpropixely: real;
    komplettebreite, komplettehoehe, seitenX, seitenY: integer;
    x,y, leftoffset, topoffset: integer;
begin

  if PrintDialog1.Execute then begin

    printer.Orientation:=poLandscape; //Drucken im Querformat
    printer.BeginDoc;
    printer.Title:='Mein Ausdruck'; //Titel, der im Druckmanager angezeigt wird
    SetMapMode(printer.Canvas.Handle, MM_LOMETRIC); // 1/10 mm

    //Druckerauflösung (mm pro Pixel) ermitteln
    mmpropixelx:=25.4/GetDeviceCaps(printer.handle, LOGPIXELSY);
    mmpropixely:=25.4/GetDeviceCaps(printer.handle, LOGPIXELSY);

    //Papiergröße ermitteln
    seitenbreite:=round(GetDeviceCaps(printer.handle, PHYSICALWIDTH)*
      MMProPixelX);
    seitenhoehe:=round(GetDeviceCaps(printer.handle, PHYSICALHEIGHT)*
      MMPropixelY);

    //nicht bedruckbare Seitenränder ermitteln
    offsetX:=round(GetDeviceCaps(printer.handle, PHYSICALOFFSETX)*MMProPixelX);
    offsetY:=round(GetDeviceCaps(printer.handle, PHYSICALOFFSETY)*MMpropixelY);

    topoffset:=0;

    //Größe unseres gesamten Ausdrucks
    komplettebreite:=gesamtX;
    komplettehoehe:=gesamtY;

    //Anzahl der "Zeilen" (seitenY) und "Spalten" (seitenX)
    seitenX:=ceil(kompletteBreite/((Seitenbreite-2*OffsetX)*10));
    seitenY:=ceil(kompletteHoehe/((Seitenhoehe-2*OffsetY)*10));

    //Schleife über die "Zeilen"
    for y:=1 to SeitenY do begin
      leftoffset:=0;

      //Schleife über die "Spalten"
      for x:=1 to SeitenX do begin
        //Verschieben des Koordinatenursprungs
        SetWindowOrgEx(printer.Handle,LeftOffset,TopOffset,nil);

        //hier normale Druck-Methode aufrufen

        //für nächste "Spalte" Abstand vom linken Rand um eine
        //Blattbreite nach rechts verschieben
        leftoffset:=leftoffset+(Seitenbreite-2*OffsetX)*10;
        if x<SeitenX then begin
          printer.NewPage;
          SetMapMode(printer.Canvas.Handle, MM_LOMETRIC);
        end;
      end;
      if y<SeitenY then begin
        printer.NewPage;
        SetMapMode(printer.Canvas.Handle, MM_LOMETRIC);
      end;

      //für nächste "Zeile" Abstand vom oberen Rand um eine
      //Blatthöhe nach unten verschieben
      topoffset:=topoffset-(Seitenhoehe-2*OffsetY)*10;
    end;
    printer.EndDoc;
  end;
end;

Der Aufruf von SetMapMode nach Printer.NewPage ist nur unter Windows 9x/Me nötig. Unter NT/2000/XP werden die Seitenattribute dagegen durch eine neue Seite nicht zurückgesetzt. Der SetMapMode-Aufruf wäre hier also nicht nötig, schadet aber auch nicht.