Printer und Papierlänge setzen

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
MmVisual
Beiträge: 1445
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Printer und Papierlänge setzen

Beitrag von MmVisual »

Hallo,

Ich bin gerade am Testen mit Lazarus 2.3.0 (Trunc von gestern Abend)...

Ich habe Label-Printer mit Endlospapier. Und das Printer Objekt, was wiederum Windows im Hintergrund nutzen muss.
Drucker 1: Brother QL-570 (Thermopapier)
Drucker 2: Brother PT2420PC (PVC Klebestreifen)

Mit Lazarus V2.0.12 hat Drucker 2 noch nicht funktioniert, das hat mich sehr gefreut dass dies nun mit V2.3.0 nun doch geht, habt ihr gut gemacht ☺.

Nun leider passt bei diesen Druckern überhaupt nichts zur Windows Spezifikation. Beide Drucker, obwohl vom gleichen Hersteller, nutzen andere Maßsysteme, solche die nicht von Microsoft deklariert wurden.
Parameter:
dmPaperLength Windows sagt es sollen 1/10mm sein
dmPaperLength Drucker 1 muss man in Pixel angeben, und noch die Printer.PaperSize.PaperRect.WorkRect.Top dazu addieren
dmPaperLength Drucker 2 ist das auch nicht richtig, da wird nur 1/3 vom Etikett gedruckt.

Gibt es eine Möglichkeit wie ich vom Drucker die Skalierung auslesen kann, so dass ich automatisiert die korrekte dmPaperLength errechnen/setzen kann?
Also "Printer.YDPI" ist nicht die Skalierung die am Parameter dmPaperLength erwartet wird, das ist nur die Umrechnung um von Pixel auf Inch im Canvas zu kommen, also wieder was anderes.
Bei Thermoetiketten ist ja das Papier immer unterschiedlich lang (ähnlich wie bei jeder Kasse beim Einkaufen, die drucken ja auch nur so lange wie man Waren kauft und nicht pauschal 100cm).

Hier die Doku von Microsoft:
https://docs.microsoft.com/en-us/window ... i-devmodea

Hier mein Code:

Code: Alles auswählen

  Procedure SetPaper(iLenghtPixel: Integer = 0);
  Begin
    Printer.Orientation := poPortrait; // Hochformat
    Printer.Canvas.Pen.Color := clBlack;
    Printer.Canvas.Brush.Color := clWhite;
    Printer.Canvas.Font.Color := clBlack;
    Printer.Canvas.Font.Name := 'Thoma';
    If iLenghtPixel > 0 Then
    Begin
      TWinPrinter(Printer).Handle := 0;
      PDev := TPrinterDevice(Printer.Printers.Objects[Printer.PrinterIndex]);
      PDev.DevModeW^.dmPaperLength := iLenghtPixel;
      PDev.DevModeW^.dmPaperSize := 256; // = DMPAPER_USER;
      PDev.DevModeW^.dmCopies := 1;
      PDev.DevModeW^.dmFields := PDev.DevModeW^.dmFields or $106; // = DM_PAPERLENGTH 4 | DM_PAPERSIZE 2 | DM_COPIES 0x100;
    End;
  end; 
Das zu druckende wird dann in das Canvas vom Printer gemalt, zuvor muss das Label die richtige Länge haben damit das Canvas auch groß genug ist.

Druck Beispiel:
Bild1.jpg
Bild1.jpg (24.41 KiB) 1480 mal betrachtet
Links "Drucker 1" / Rechts "Drucker 2", Log:
Printer: Brother QL-570 DPI X 300 Y 300 PaperLen 17,2 mm
Printer: Brother PT-2420PC DPI X 180 Y 180 PaperLen 19,9 mm
Obwohl die Funktion ein größeres "PaperLen" ausrechnet (weil der Code ist auch etwas breiter) wird dennoch nicht alles gedruckt, bzw. Abgeschnitten.
Wenn ich bei "Drucker 2" die Länge *2 setze, dann ist der ganze 2D Code drauf. Als ob da noch irgend ein Offset Wert fehlt, von ca. 15mm.

Dankeschön für die Hilfe
Viele Grüße Markus
EleLa - Elektronik Lagerverwaltung - www.elela.de

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 168
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: Printer und Papierlänge setzen

Beitrag von Jorg3000 »

Hi!
Ich habe nur eine bloße Vermutung: Da du schreibst "nur 1/3 vom Etikett gedruckt" bzw. die Länge *2 setzten musst,
kommt mir 2,54 cm als 1 Inch in den Sinn (weil es genau zwischem dem o.g. Faktor 2 bzw. 3 liegt).

Könnte es sein, dass du bei einem der Drucker die 1/10mm in Inch umrechnen musst, damit es passt?
Grüße, Jörg

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Printer und Papierlänge setzen

Beitrag von Winni »

Hi!

Ich hab sowas immer getestet , bevor ich mit Meilen und Unzen rechnen muss:

Code: Alles auswählen

var i, y,hoch : integer;
.....
Hoch := printer.Canvas.Font.height;
y := 0;
For i := 0 to 100 do
   begin
   Printer.Canvas.TextOut(0,y,intToStr(i)+' * '+IntToStr(hoch) );
   inc(y,hoch);
end;
...
Dann wollen mal sehen, wie viele Zeilen drauf gehen.

Winni

MmVisual
Beiträge: 1445
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: Printer und Papierlänge setzen

Beitrag von MmVisual »

Hallo,

In der Tat muss alles 2x berechnet werden, das erste mal um überhaupt zu wissen wie viele Pixels man hat und dann kann man die Papiergröße im Drucker einstellen, dann erst kann man das in das Canvas vom Drucker malen.

Die Einstellungen vom Drucker:
Bild1.png
Bild1.png (10.3 KiB) 1405 mal betrachtet
Bild2.png
Bild2.png (6.88 KiB) 1405 mal betrachtet
Vom ganzen druckbaren Bereich muss man den "Bandvorschub" von 4mm mit dazu rechnen, da in diesem Bereich nicht gedruckt wird. Die "Einheit" lässt sich nicht auf "Zoll" einstellen, da springt der Treiber immer wieder auf mm zurück.

Hier der Quellcode:
Im Wesentlichen die Größe vom DataMatrix Code lesen, einstellen und errechnen wie viele Pixel man braucht und noch korrekturen damit der Code einerseits optimal lesbar ist und auch in die Breite vom Label passt.
Dann die Papierlänge einstellen und den Code erneut generieren in ein neues Bitmap. Dann geht es mit Printer.BeginDoc los, nur noch das BM in das Canvas malen.
iLines + 2 ist nötig, damit außerhalb vom Datamatrix Code auch noch ein weißer Rand ist, sonst erkennt das der Scanner nicht.

Code: Alles auswählen

  bm := TBitmap.Create;
  bm.SetSize(100, 100);
  iLines := BuildDatamatrix(bm, s2DCode);
  If iLines <= 0 Then // Kein Barcode erzeugt
  Begin
    FreeAndNilMm(bm);
    Exit;
  End;

  Printer.Orientation := poPortrait;
  If Not PrintDlg.Execute Then
  Begin
    bm.Height := (iLines + 2) * 4;
    bm.Width := bm.Height;
    iLines := BuildDatamatrix(bm, s2DCode);
    Clipboard.Assign(bm); // Bei Abbruch von Druck-Dialog Datamatrix Code in Zwischenablage kopieren
    FreeAndNilMm(bm);
    Exit;
  end;

  // Berechnung Pixelgröße für DataMatrix:
  // Min: 0,254mm
  // Avg: 0,380mm
  // Max: 0,495mm
  SetPaper(0); // Orientierung setzen
  xDPmm := Printer.XDPI / 25.4; // Breite
  yDPmm := Printer.YDPI / 25.4; // Höhe
  rc1 := Printer.PaperSize.PaperRect.WorkRect;
  rc2 := Printer.PaperSize.PaperRect.PhysicalRect;

  fPix := GetParamFloat(25);
  If fPix < 0.254 Then
    i2DLinePixel := Round(xDPmm * 0.495) // Pixel pro mm * 0,495mm
  Else i2DLinePixel := Round(xDPmm * fPix); // Pixel pro mm * <Parameter 25>
  If Double(i2DLinePixel) < (xDPmm * 0.254) Then
    Inc(i2DLinePixel);
  If (i2DLinePixel < 6) And (fPix < 0.254) Then // 6 Pixel sollen je Dot schon sein, damit es gut raus kommt
    i2DLinePixel := 6;
  If i2DLinePixel > (rc1.Width Div (iLines + 2)) Then // Grafik ist größer -> Pxel verkleinern
    i2DLinePixel := rc1.Width Div (iLines + 2);
  bm.Width := i2DLinePixel * (iLines + 2);
  bm.Height := bm.Width;

  If Pos('Brother PT-', Printer.PrinterName) = 1 Then
    iLen := ((bm.Height + (i2DLinePixel * 2)) * 2) + (Printer.PaperSize.PaperRect.PhysicalRect.Height - rc1.Height) // Länge setzen *2 für P-Touch Drucker
  Else iLen := bm.Height + (i2DLinePixel * 2) + rc1.Top;
  SetPaper(iLen); // Länge setzen
  DoLog('Printer: ' + Printer.PrinterName + '  DPI X ' + Printer.XDPI.ToString + '  Y ' + Printer.YDPI.ToString + '  PaperLen ' + FormatFloat('0.#', iLen / yDPmm) + ' mm');

  iLines := BuildDatamatrix(bm, s2DCode);

  Printer.BeginDoc;
Nun die Werte während dem Debuggen:
Printer.PaperSize.PaperRect.WorkRect:
Bild3.png
Bild3.png (2.07 KiB) 1405 mal betrachtet
Printer.PaperSize.PaperRect.PhysicalRect:
Bild4.png
Bild4.png (1.9 KiB) 1405 mal betrachtet
bm.Height = 104;
iLen := 282;
Log: 19:16:11 Printer: Brother PT-2420PC DPI X 180 Y 180 PaperLen 39,8 mm

Hier das Ergebnis nach dem Druck, Lineal an den ersten Punkten der "Schnittmarkierung" angelegt:
Bild5.jpg
Bild5.jpg (49.29 KiB) 1405 mal betrachtet
Also die 282 Pixel / 180 DPI = 1,57 Inch * 25,4 = 39,8mm.
Irgendwie passt das überhaupt nicht zum Ergebnis.

Normalerweise sollte die Höhe sein: 112 + 29 = 141 Pixel = 19,9mm, Und nun erwartet Windws 1/10mm als Parameter, dann würde hier 199 übertragen werden müssen, dann fehlt jedoch etwas vom Code.

Wenn ich nun so rechne: 112 + 58 = 170 Pixel = 24mm > 240, dann scheint es nun doch zu passen. :)
Also man muss die PhysicalRect.Height - WorkRect.Height in Pixel dazu rechnen.

Hingegen: rc1.Top ist 29, also der Druckbereich beginnt bei 4mm (Bandvorschub), das umgerechnet in DPI sind etwa die 180, das würde stimmen.

Soweit die Zusammenfassung...

VG Markus
EleLa - Elektronik Lagerverwaltung - www.elela.de

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Printer und Papierlänge setzen

Beitrag von Winni »

Hi!

Nicht über die Ergebnisse wundern: Die Druckertreiber müssen anscheinend immer die Azubis programmieren. Die Firma Brother ist dafür ein "leuchtendes" Beispiel. HP und andere haben da auch richtig Mist geschnitzt.

Testen und die Werte in ne Ini-Datei schreiben. Bis zum nächsten Model.

Winni

PS.: Nicht Drucker aber Brother: Die sind zu doof bei ihrer Fax-/Scan-/Druck-Maschine Zeitzone, automatische Sommerzeit und angegebene Uhrzeit richtig zusammen zu rechnen. Ich hab die Kiste neulich auf UTC0 gestellt, damit die richtige Uhrzeit auf den Faxen ist.

Antworten