Einen String oder Edit-Control zum Drucker senden

Stelle Fragen zur Programmierung mit Autohotkey

Moderator: jNizM

User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Einen String oder Edit-Control zum Drucker senden

24 Mar 2016, 04:58

Hey,

Ich versuche einen String (mehrzeilig oder Gui-Edit-Control mit mehreren Zeilen) zu einem Drucker zu senden (ausdrucken).

Der Originale Code (von Lexikos - bearbetet von mir für den 64-Bit support) funktioniert mit 1-Zeilern (verwendet wird die TextOut function):

Code: Select all

Print_TextOut("Test_Document", "Hello World")

Print_TextOut(Document_Name, Document_Text)
{
    NumPut(VarSetCapacity(PD, (A_PtrSize = 8 ? (A_PtrSize * 13) + 16 : 66), 0), PD)
    NumPut(0x0100, PD, A_PtrSize * 5, "UInt")
    if !(DllCall("comdlg32.dll\PrintDlg", "Ptr", &PD, "Int"))
        MsgBox % "Error: " A_LastError
    DllCall("GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 2, "UPtr"))
    DllCall("GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 3, "UPtr"))

    if (hDC := NumGet(PD, A_PtrSize * 4, "UPtr"))
    {
        NumPut(VarSetCapacity(DI, A_PtrSize * 5, 0), DI, 0, "UInt")
        NumPut(&Document_Name, DI, A_PtrSize, "UPtr")
        if (DllCall("gdi32.dll\StartDoc", "Ptr", hDC, "Ptr", &DI, "Int") > 0)
        {
            if (DllCall("gdi32.dll\StartPage", "Ptr", hDC, "Int") > 0)
            {
                xdpi := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x58) / 2.54
                ydpi := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x5A) / 2.54
                DllCall("gdi32.dll\TextOut", "Ptr", hDC, "Int", xdpi, "Int", ydpi, "Ptr", &Document_Text, "Int", StrLen(Document_Text))
                DllCall("gdi32.dll\EndPage", "Ptr", hDC, "Int")
            }
            DllCall("gdi32.dll\EndDoc", "Ptr", hDC)
        }
        DllCall("gdi32.dll\DeleteDC", "Ptr", hDC)
    }
}
Image

Mein Problem aber ist, das drucken von Mehr-Zeilern oder Edit-Controls mit mehreren Zeilen (verwendet wird hier DrawText function):
ref: New line with TextOut function

Code: Select all

Print_DrawText("Test_Document", "Hello\r\nWorld")

Print_DrawText(Document_Name, Document_Text)
{
    NumPut(VarSetCapacity(PD, (A_PtrSize = 8 ? (A_PtrSize * 13) + 16 : 66), 0), PD)
    NumPut(0x0100, PD, A_PtrSize * 5, "UInt")
    if !(DllCall("comdlg32.dll\PrintDlg", "Ptr", &PD, "Int"))
        MsgBox % "Error: " A_LastError
    DllCall("GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 2, "UPtr"))
    DllCall("GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 3, "UPtr"))

    if (hDC := NumGet(PD, A_PtrSize * 4, "UPtr"))
    {
        NumPut(VarSetCapacity(DI, A_PtrSize * 5, 0), DI, 0, "UInt")
        NumPut(&Document_Name, DI, A_PtrSize, "UPtr")
        if (DllCall("gdi32.dll\StartDoc", "Ptr", hDC, "Ptr", &DI, "Int") > 0)
        {
            if (DllCall("gdi32.dll\StartPage", "Ptr", hDC, "Int") > 0)
            {
                xdpi := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x58) / 2.54
                ydpi := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x5A) / 2.54
                VarSetCapacity(RECT, 16, 0), DllCall("user32.dll\SetRect", "Ptr", &RECT, "Int", xdpi, "Int", ydpi, "Int", 1200, "Int", 1200)
                DllCall("user32.dll\DrawText", "Ptr", hDC, "Ptr", &Document_Text, "Int", -1, "Ptr", &RECT, "UInt", (0x0|0x200|0x10))
                DllCall("gdi32.dll\EndPage", "Ptr", hDC, "Int")
            }
            DllCall("gdi32.dll\EndDoc", "Ptr", hDC)
        }
        DllCall("gdi32.dll\DeleteDC", "Ptr", hDC)
    }
}
Image

+ Wie berechne ich aber die korrekte xRight und yBottom größe für den Struct wenn der String sich jedes mal von der größe und länge her verändert?

Gelöst:
- Mehrzeilig drucken (mit `r`n anstatt \r\n Print_DrawText("Test_Document", "Hello`r`nWorld"))
Image

Noch offen:
- Korrekte Berechnung des RECT structure xRight & yBottom
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Send a string (or edit control) to a printer

24 Mar 2016, 06:10

Nach den ersten Tests mit FillRgn function (einfärben des RECT's):

Drucken zu einem normalem Drucker (Drucker hat eigenen Margin)

Code: Select all

Print("Test_Document", "Hello`r`nWorld")

Print(Document_Name, Document_Text)
{
    NumPut(VarSetCapacity(PD, (A_PtrSize = 8 ? (A_PtrSize * 13) + 16 : 66), 0), PD)
    NumPut(0x0100, PD, A_PtrSize * 5, "UInt")
    if !(DllCall("comdlg32.dll\PrintDlg", "Ptr", &PD, "Int"))
        MsgBox % "Error: " A_LastError
    DllCall("GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 2, "UPtr"))
    DllCall("GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 3, "UPtr"))

    if (hDC := NumGet(PD, A_PtrSize * 4, "UPtr"))
    {
        ; CALC
        PHYSICALWIDTH   := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x6E, "Int") ; 110 - Physical Width in device units
        PHYSICALHEIGHT  := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x6F, "Int") ; 111 - Physical Height in device units
        PHYSICALOFFSETX := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x70, "Int") ; 112 - Physical Printable Area x margin
        PHYSICALOFFSETY := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x71, "Int") ; 113 - Physical Printable Area y margin
        VarSetCapacity(RECT, 16, 0)
        NumPut(PHYSICALOFFSETX, RECT, 0, "Int")
        NumPut(PHYSICALOFFSETY, RECT, 4, "Int")
        NumPut(PHYSICALWIDTH - PHYSICALOFFSETX, RECT,  8, "Int")
        NumPut(PHYSICALHEIGHT - PHYSICALOFFSETY, RECT, 12, "Int")

        ; PRINT
        NumPut(VarSetCapacity(DI, A_PtrSize * 5, 0), DI, 0, "UInt")
        NumPut(&Document_Name, DI, A_PtrSize, "UPtr")
        if (DllCall("gdi32.dll\StartDoc", "Ptr", hDC, "Ptr", &DI, "Int") > 0)
        {
            if (DllCall("gdi32.dll\StartPage", "Ptr", hDC, "Int") > 0)
            {
                /*
                hRGN  := DllCall("gdi32.dll\CreateRectRgnIndirect", "Ptr", &RECT, "UPtr")
                hBR := DllCall("gdi32.dll\CreateSolidBrush", "Int", 0xA0A0A0, "UPtr")
                DllCall("gdi32.dll\FillRgn", "Ptr", hDC, "Ptr", hRGN, "Ptr", hBR)
                */
                DllCall("user32.dll\DrawText", "Ptr", hDC, "Ptr", &Document_Text, "Int", -1, "Ptr", &RECT, "UInt", (0x0|0x200|0x10))
                DllCall("gdi32.dll\EndPage", "Ptr", hDC, "Int")
            }
            DllCall("gdi32.dll\EndDoc", "Ptr", hDC)
        }
        DllCall("gdi32.dll\DeleteDC", "Ptr", hDC)
    }
}
Drucken zu XPS-Drucker (Drucker hat kein eigenen Margin)

Code: Select all

Print_XPS("Test_Document", "Hello`r`nWorld")

Print_XPS(Document_Name, Document_Text)
{
    NumPut(VarSetCapacity(PD, (A_PtrSize = 8 ? (A_PtrSize * 13) + 16 : 66), 0), PD)
    NumPut(0x0100, PD, A_PtrSize * 5, "UInt")
    if !(DllCall("comdlg32.dll\PrintDlg", "Ptr", &PD, "Int"))
        MsgBox % "Error: " A_LastError
    DllCall("GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 2, "UPtr"))
    DllCall("GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 3, "UPtr"))

    if (hDC := NumGet(PD, A_PtrSize * 4, "UPtr"))
    {
        ; CALC
        LOGPIXELSX      := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x58, "Int") ;  88 - Logical pixels/inch in X
        LOGPIXELSY      := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x5A, "Int") ;  90 - Logical pixels/inch in Y
        PHYSICALWIDTH   := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x6E, "Int") ; 110 - Physical Width in device units
        PHYSICALHEIGHT  := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x6F, "Int") ; 111 - Physical Height in device units
        VarSetCapacity(RECT, 16, 0)
        NumPut(LOGPIXELSX / 2.54, RECT, 0, "Int")
        NumPut(LOGPIXELSY / 2.54, RECT, 4, "Int")
        NumPut(PHYSICALWIDTH - (LOGPIXELSX / 2.54), RECT,  8, "Int")
        NumPut(PHYSICALHEIGHT - (LOGPIXELSY / 2.54), RECT, 12, "Int")

        ; PRINT
        NumPut(VarSetCapacity(DI, A_PtrSize * 5, 0), DI, 0, "UInt")
        NumPut(&Document_Name, DI, A_PtrSize, "UPtr")
        if (DllCall("gdi32.dll\StartDoc", "Ptr", hDC, "Ptr", &DI, "Int") > 0)
        {
            if (DllCall("gdi32.dll\StartPage", "Ptr", hDC, "Int") > 0)
            {
                /*
                hRGN  := DllCall("gdi32.dll\CreateRectRgnIndirect", "Ptr", &RECT, "UPtr")
                hBR := DllCall("gdi32.dll\CreateSolidBrush", "Int", 0xA0A0A0, "UPtr")
                DllCall("gdi32.dll\FillRgn", "Ptr", hDC, "Ptr", hRGN, "Ptr", hBR)
                */
                DllCall("user32.dll\DrawText", "Ptr", hDC, "Ptr", &Document_Text, "Int", -1, "Ptr", &RECT, "UInt", (0x0|0x200|0x10))
                DllCall("gdi32.dll\EndPage", "Ptr", hDC, "Int")
            }
            DllCall("gdi32.dll\EndDoc", "Ptr", hDC)
        }
        DllCall("gdi32.dll\DeleteDC", "Ptr", hDC)
    }
}

- Ich arbeite noch an beiden
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Send a string (or edit control) to a printer

25 Mar 2016, 04:12

Hi jNizM,

as long as you set margins wider than PHYSICALOFFSETX / PHYSICALOFFSETY your script should print the whole text if it fits into one page. Otherwise some text will be clipped at the bottom respectively not printed if the rectangle exceeds the width of the printable area.

Seemingly, HORZRES / VERTRES retrieve the width / height of the printable area for printing devices.

If you have further questions, switch to the German forum, please. It makes quite a bit effort to explain it in English.
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Einen String oder Edit-Control zum Drucker senden

29 Mar 2016, 01:48

So, ich habs mal verschoben und wieder ins deutsche übersetzt.
In deiner RichEdit Klasse, ist das mit dem Margin denk ich mal schon alles berechnet und habe deswegen schon mal angefangen das in eine eigene Klasse zu schreiben.

Code: Select all

Class Print
{
    XPS()
    {
        NumPut(VarSetCapacity(PD, (A_PtrSize = 8 ? (A_PtrSize * 13) + 16 : 66), 0), PSD, "UInt")
        NumPut(0x0100, PD, A_PtrSize * 5, "UInt")
        if !(DllCall("comdlg32.dll\PrintDlg", "Ptr", &PD, "Int"))
            return False
        DllCall("GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 2, "UPtr"))
        DllCall("GlobalFree", "Ptr", NumGet(PD, A_PtrSize * 3, "UPtr"))
        /*
        ...
        */
    }

    GetPrinterMargins()
    {
        NumPut(VarSetCapacity(PSD, (A_PtrSize = 8 ? (A_PtrSize * 13) + 16 : 66), 0), PSD, "UInt")
        NumPut(0x00000400, PSD, A_PtrSize * 6, "UInt")
        if !(DllCall("comdlg32.dll\PageSetupDlg", "Ptr", &PSD))
            return False
        DllCall("GlobalFree", "Ptr", NumGet(PSD, A_PtrSize * 2, "UPtr"))
        DllCall("GlobalFree", "Ptr", NumGet(PSD, A_PtrSize * 3, "UPtr"))
        /*
        ...
        */
    }

    GetPrinterCaps(hDC)
    {
        GDC := {}
        GDC.HORZRES         := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x08, "Int")
        GDC.VERTRES         := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x0A, "Int")
        GDC.LOGPIXELSX      := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x58, "Int")
        GDC.LOGPIXELSY      := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x5A, "Int")
        GDC.PHYSICALWIDTH   := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x6E, "Int")
        GDC.PHYSICALHEIGHT  := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x6F, "Int")
        GDC.PHYSICALOFFSETX := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x70, "Int")
        GDC.PHYSICALOFFSETY := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", 0x71, "Int")
        return GDC
    }
}
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Einen String oder Edit-Control zum Drucker senden

29 Mar 2016, 02:56

Moin jNizM,

Du warst ja ein paar Tage nicht anwesend, und weil mich das Thema "Drucken mit AHK" schon sehr lange interessiert, habe ich mich inzwischen intensiv mit diesem Ansatz beschäftigt. Ich weiß nicht, ob ich den Beitrag von Lexikos vorher je gesehen habe. Wenn ja, hat er mir zumindest 2007 überhaupt nichts gesagt, weil ich damals nicht in der Lage war, die erforderllichen DllCalls umzusetzen. Und als ich später den RichEdit-Druck so halbwegs im Griff hatte, habe ich mich nie wieder mit Alternativen beschäftigt. Von Heute aus betrachtet würde ich sagen: "Wenn ich gewusst hätte, dass das 'so einfach' ist, ..."

Ich will dennoch erst einmal das fortsetzen, was ich mit meiner Antwort ursprünglich im Sinn hatte. Es wird aber ein längerer Text werden und braucht noch etwas Zeit.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Einen String oder Edit-Control zum Drucker senden

29 Mar 2016, 05:55

So, weiter geht's!

Theorie:

Um den maximalen Druckbereich zu bestimmen, kann man folgende Werte aus GetDeviceCaps() nutzen:

Code: Select all

HORZRES           8 /* Horizontal width in pixels               */
VERTRES          10 /* Vertical height in pixels                */
LOGPIXELSX       88 /* Logical pixels/inch in X                 */
LOGPIXELSY       90 /* Logical pixels/inch in Y                 */
PHYSICALWIDTH   110 /* Physical Width in device units           */
PHYSICALHEIGHT  111 /* Physical Height in device units          */
PHYSICALOFFSETX 112 /* Physical Printable Area x margin         */
PHYSICALOFFSETY 113 /* Physical Printable Area y margin         */
Wie Lexikos hier bereits gesagt hat, werden die 'device units' ebenfalls als Pixel geliefert.
  • HORZRES/VERTRES liefern die Breite bzw die Höhe des bedruckbaren Bereichs einer Seite.
  • LOGPIXELSX/LOGPIXELSY liefern die "Pixel per Inch" in horizontaler bzw. vertikaler Richtung, d.h. die Auflösung.
  • PHYSICALWIDTH/PHYSICALHEIGHT liefern die physische Breite bzw. Höhe des Druckmediums.
  • PHYSICALOFFSETX/PHYSICALOFFSETY liefern den Abstand des bedruckbaren Bereichs vom physischen Seitenrand in horizontaler bzw. vertikaler Richtung.
  • Die unteren bzw. rechten Abstände kann man wie folgt berechnen:
    Rechts = PHYSICALWIDTH - HORZRES - PHYSICALOFFSETX
    Unten = PHYSICALHEIGHT - VERTRES - PHYSICALOFFSETY
Wie Lexikos ebenfalls gesagt hat, kann die Function TextOut() keine Zeilen umbrechen. Er hat empfohlen, stattdessen eine 'leistungsfähigere' Funktion wie DrawText() zu verwenden. Das ist schon mal ganz gut, wenn der Text auf eine Seite passt. Passt er nicht, muss man allerdings die 'noch leistungsfähigere' Funktion DrawTextEx() verwenden. Die hat den Vorteil, dass sie im zusätzlichen Parameter DRAWTEXTPARAMS zurückgibt, bis zu welchem Zeichen sie gekommen ist, bevor Text abgeschnitten wurde. Wenn dieser Wert nicht der Länge der übergebenen Zeichenfolge entspricht, konnte ein Teil des Textes nicht gedruckt werden. Er muss auf einer weiteren Seite ausgegeben werden.

Der Funktion DrawTextEx() muss man ein Rechteck übergeben, in das der Text ausgegeben werden soll. Die Koordinaten dieses Rechtecks beziehen sich anscheinend auf den bedruckbaren Bereich der Seite, d.h. 0, 0, HORZRES, VERTRES füllt jedenfalls den gesamten verfügbaren Druckbereich.

Ränder werden bis auf den von England beeinflussten Bereich der Welt üblicherweise in mm/cm gesetzt. Die Angaben für das Rechteck benötigen aber Pixel. Wieviele Pixel ergeben aber 1 cm? Hier helfen die Werte LOGPIXELSX/LOGPIXELSY weiter, die die Auflösung in "Pixel per Inch" liefern. Ein Inch/Zoll entspricht etwa 2,54 cm. Teilt man den Wert aus LOGPIXELSX durch 2,54, erhält man die Anzahl der Pixel für einen nahezu 1 cm breiten horizontalen Rand. Wenn man Seitenränder setzen will, beziehen die sich aber üblicherweise auf den physischen Seitenrand. Will man das Rechteck entsprechend anpassen, muss man deshalb die Distanzen zwischen physischem Rand und Druckbereich abziehen.

Praxis:

Das hier habe ich inzwischen für die Druckausgabe auf den Standarddrucker entwickelt. Der Funktion PrintStr() werden ein String und optional ein Font übergeben. Die Funktion versucht, den übergebenen Font möglichst genau an die Druckerauflösung angepasst für den Ausdruck zu nutzen; Standard ist Arial 10 Punkt. Außerdem setzt die Funktion hartcodierte Seitenränder von etwa 20 mm. Zeilen und Seitenumbrüche scheinen recht zuverlässig zu funktionieren. Ich musste dafür aber das Flag DT_EDITCONTROL setzen. Ansonsten können am Ende der Seite Teile von Zeilen gedruckt werden, wenn das Rechteck nicht genau passt.

Code: Select all

#NoEnv
SetBatchLines -1
Lorem =
(Join
Lorem ipsum dolor sit amet consectetuer nulla vitae et felis nonummy. Tempor quis volutpat risus consectetuer Phasellus et quam
 congue nec est. Volutpat Sed Vestibulum ridiculus montes tincidunt ac Pellentesque tempor leo Duis. Amet wisi at pretium et
 faucibus semper at Curabitur pretium at. Et Morbi Nullam tincidunt condimentum at nunc egestas Maecenas leo et. Dolor eros montes
 In Morbi dignissim consequat lacinia amet ut Duis. Et leo eros.
`r`n`r`n
Turpis volutpat sodales feugiat odio quis id netus facilisis ac a. Cursus congue dolor urna urna pellentesque tellus nascetur
 facilisis Sed laoreet. Sem lacus porta id wisi consectetuer id Donec elit at.
`r`n`r`n
Cursus vel non feugiat at Aenean interdum nec tellus Ut Donec. Tortor Aliquam sit dui Vivamus nec dui dapibus metus amet feugiat.
 Ac ridiculus Donec ipsum et et Curabitur leo mollis sagittis vitae. Facilisis Nam nec tellus velit tincidunt dapibus ac adipiscing.
`r`n`r`n
Penatibus In netus tristique egestas tincidunt risus risus malesuada convallis tellus. Facilisi Sed Maecenas ultrices sem auctor
 netus scelerisque accumsan ac egestas. Neque vel elit a enim euismod ac vitae tincidunt porttitor laoreet. Interdum urna nibh at
 nunc aliquet Fusce hac semper lacinia elit. Tellus tortor Ut sapien interdum orci vel enim sed Nam arcu. Nam id justo mauris nunc
 Donec justo id pede Lorem lacinia. Eget parturient turpis Donec consequat tempus lobortis tortor id Suspendisse Nam. Justo amet.
)
EditText := Lorem
Loop, 3
   EditText .= "`r`n`r`n" . Lorem
; ----------------------------------------------------------------------------------------------------------------------------------
Gui, Margin, 10, 10
Gui, Font, s10, Arial
Gui, Add, Edit, xm ym w600 r20 hwndHED, %EditText%
Gui, Add, Button, gPrint vBtnPrint Default, Print
GuiControl, Focus, BtnPrint
Gui, Show, , Print Edit
Return
; ----------------------------------------------------------------------------------------------------------------------------------
GuiClose:
GuiEscape:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------------------
Print:
ControlGetText, Str, , ahk_id %HED% ; it's important to use ControlGetText if you read the text from an Edit control
If (Str) {
   If !PrintStr(Str)
      MsgBox, 0, PrintStr, An error occurred!`nErrorLevel = %ErrorLevel%
}
Return
; ==================================================================================================================================
; Prints the passed string on the default printer
; Parameters:
;     Str      -  string to print
;     FontName -  name of the font to be used for printing like the FontName parameter of the 'Gui, Font' command
;                 Default: Arial
;     FontSize -  size of the font in points
;                 Default: 10
;     FontOpts -  other font options like the Options parameter of the 'Gui, Font' command
;                 Default: s10
; Return values:
;     On success: True
;     On failure: False
;                 Errorlevel contains one of the following values:
;                 1 - the specified font is not available
;                 2 - error when calling the PrintDlg API function
;                 3 - couldn't retrieve a valid DC for the printer
;                 4 - an error occured while trying to print
; Remarks:
;     Based on the code published by Lexikos at autohotkey.com/board/topic/20468-detecting-printer-printing-text/page-2#entry146062
; ==================================================================================================================================
PrintStr(Str, FontName := "Arial", FontSize := 10, FontOpts := "") {
   Static DISize := A_PtrSize * 5 ; size of DOCINFO structure
   Static DPSize := 20 ; size of DRAWTEXTPARAMS structure
   Static LFSize := A_IsUnicode ? 60 : 92 ; size of LOGFONT structure
   Static PDSize := A_PtrSize = 8 ? (A_PtrSize * 13) + 16 : 66 ; size of PRINTDLG structure
   Static Margins := 20 ; left, top, right, and bottom margins in millimeters
   Static DocName := "PrintStr" ; document name
   ; Get a HFONT handle and the LOGFONT structure for the passed font (lazy method)
   Gui, PrintStrGUI: Font, %FontOpts% q2 s%FontSize%, %FontName%
   Gui, PrintStrGUI: Add, Text, hwndHTX, Dummy!
   HFONT := DllCall("SendMessage", "Ptr", HTX, "UInt", 0x0031, "Ptr", 0, "ptr", 0, "UPtr") ; WM_GETFONT
   Gui, PrintStrGUI: Destroy
   VarSetCapacity(LOGFONT, LFSize, 0) ; LOGFONT
   DllCall("GetObject", "Ptr", HFONT, "Int", LFSize, "Ptr", &LOGFONT)
   If (FontName <> StrGet(&LOGFONT + 28))
      Return (ErrorLevel := 1) & 0
   ; Get a device context of the default printer
   VarSetCapacity(PRINTDLG, PDSize, 0) ; PRINTDLG
   NumPut(PDSize, PRINTDLG, 0, "UInt")
   NumPut(0x0100 | 0x0400, PRINTDLG, A_PtrSize * 5, "UInt")
   If !(DllCall("Comdlg32.dll\PrintDlg", "Ptr", &PRINTDLG, "Int"))
      Return (ErrorLevel := 2) & 0
   DllCall("GlobalFree", "Ptr", NumGet(PRINTDLG, A_PtrSize * 2, "UPtr"))
   DllCall("GlobalFree", "Ptr", NumGet(PRINTDLG, A_PtrSize * 3, "UPtr"))
   If !(HDC := NumGet(PRINTDLG, A_PtrSize * 4, "UPtr"))
      Return (ErrorLevel := 3) & 0
   ; Get the device specific values
   DPIX := DllCall("GetDeviceCaps", "Ptr", HDC, "Int", 0x58)     ; LOGPIXELSX (horizontal resolution)
   , DPIY := DllCall("GetDeviceCaps", "Ptr", HDC, "Int", 0x5A)   ; LOGPIXELSY (vertical resolution)
   , PageW := DllCall("GetDeviceCaps", "Ptr", HDC, "Int", 0x08)  ; HORZRES (width of the printable area)
   , PageH := DllCall("GetDeviceCaps", "Ptr", HDC, "Int", 0x0A)  ; VERTRES (height of the printable area)
   , PhysW := DllCall("GetDeviceCaps", "Ptr", HDC, "Int", 0x6E)  ; PHYSICALWIDTH (physical width in device units)
   , PhysH := DllCall("GetDeviceCaps", "Ptr", HDC, "Int", 0x6F)  ; PHYSICALHEIGHT (physical height in device units)
   , OffsL := DllCall("GetDeviceCaps", "Ptr", HDC, "Int", 0x70)  ; PHYSICALOFFSETX (physical printable area x margin)
   , OffsT := DllCall("GetDeviceCaps", "Ptr", HDC, "Int", 0x71)  ; PHYSICALOFFSETY (physical printable area y margin)
   , OffsR := PhysW - PageW - OffsL                              ; physical printable area right offset
   , OffsB := PhysH - PageH - OffsT                              ; physical printable area bottom offset
   , HorzM := Round((DPIX / 25.4) * Margins)                     ; horizontal margins (~ 20 mm)
   , VertM := Round((DPIY / 25.4) * Margins)                     ; vertical margins (~ 20 mm)
   ; Set the printing rectangle
   RectL := HorzM - OffsL ; left
   , RectT := VertM - OffsT ; top
   , RectR := PageW - HorzM + OffsR ; right
   , RectB := PageH - VertM + OffsB ; bottom
   ; Scale the font height according to the vertical resolution of the printer
   FontH := NumGet(LOGFONT, 0, "Int") ; lfHeight
   , NumPut(Round(-FontSize * DPIY / 72), LOGFONT, 0, "Int")
   , NumPut(0, LOGFONT, 4, "Int") ; lfWidth
   , HFONT := DllCall("CreateFontIndirect", "Ptr", &LOGFONT, "UPtr")
   ; Select the scaled font
   DllCall("SelectObject", "Ptr", HDC, "Ptr", HFONT)
   ; Prepare for printing
   VarSetCapacity(DOCINFO, DISize, 0) ; DOCINFO
   , NumPut(DISize, DOCINFO, "UInt")
   , NumPut(&DocName, DOCINFO, A_PtrSize, "UPtr")
   VarSetCapacity(DTPARAMS, DPSize, 0) ; DRAWTEXTPARAMS
   , NumPut(DPSize, DTPARAMS, "UInt")
   VarSetCapacity(RECT, 16, 0)
   , NumPut(RectL, RECT, 0, "Int")
   , NumPut(RectT, RECT, 4, "Int")
   , NumPut(RectR, RECT, 8, "Int")
   , NumPut(RectB, RECT, 12, "Int")
   RC := 0
   ; Print
   If DllCall("StartDoc", "Ptr", HDC, "Ptr", &DOCINFO, "UInt") {
      Loop {
         Len := StrLen(Str)
         If DllCall("StartPage", "Ptr", HDC, "Int") {
            RC := DllCall("DrawTextEx", "Ptr", HDC, "Ptr", &Str, "Int", Len, "Ptr", &RECT, "UInt", 0x2250, "Ptr", &DTPARAMS, "Int")
            DllCall("EndPage", "Ptr", HDC, "Int")
         }
         If (RC) {
            LD := NumGet(DTPARAMS, 16, "UInt") ; uiLengthDrawn
            Str := LD < Len ? SubStr(Str, LD + 1) : ""
         }
      } Until (RC = 0) || (Str = "")
      DllCall("EndDoc", "Ptr", HDC)
   }
   DllCall("DeleteDC", "Ptr", HDC)
   If (HFONT)
      DllCall("DeleteObject", "Ptr", HFONT)
   Return (RC ? 1 : (ErrorLevel := 4) & 0)
}
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Einen String oder Edit-Control zum Drucker senden

29 Mar 2016, 06:50

Wow... Sieht sehr gut aus. Getestet mit einem Laser- und XPS-Drucker.

Wenn man aus NumPut(0x0100 | 0x0400, PRINTDLG, A_PtrSize * 5, "UInt") noch NumPut(0x0100, PRINTDLG, A_PtrSize * 5, "UInt") macht, kann man auch den Drucker manuell auswählen (wenn mehrere vorhanden sind), ansonsten wird auf dem Standarddrucker ausgedruckt.

Eine Verständnis-Frage habe ich noch zwecks RECT
95% im Forum benutzen ja immer:

Code: Select all

VarSetCapacity(RECT, 16, 0)
NumPut(RectL, RECT,  0, "Int")
NumPut(RectT, RECT,  4, "Int")
NumPut(RectR, RECT,  8, "Int")
NumPut(RectB, RECT, 12, "Int")
Aber theoretisch geht ja auch (SetRect function):

Code: Select all

VarSetCapacity(RECT, 16, 0)
DllCall("user32.dll\SetRect", "Ptr", &RECT, "Int", RectL, "Int", RectT, "Int", RectR, "Int", RectB)
Wo liegt jetzt hier der Vor- und/oder Nachteil?
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Einen String oder Edit-Control zum Drucker senden

29 Mar 2016, 07:15

Im Endeffekt ist das Ergebnis das gleiche, bloß dass A jeder der AutoHotkey ein bisschen versteht sofort klar ist.
Beim 2. müsste man ein Blick in die WinApi werfen.
Recommends AHK Studio
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Einen String oder Edit-Control zum Drucker senden

29 Mar 2016, 08:32

Ich denke, dass durch den Funktionsnamen in beiden Fällen klar ist, was gemacht wird. Der DllCall spart ein wenig Platz. Die 4 Numgets mögen schneller sein, zumindest wenn man sie 'verkommat'. Den Unterschied wird man aber wohl nur in Ausnahmefällen spüren. Und innerhalb einer Druckfunktion dürfte er kaum messbar sein.
User avatar
Gucky_87
Posts: 375
Joined: 03 Jul 2014, 05:09

Re: Einen String oder Edit-Control zum Drucker senden

13 Jan 2017, 06:47

Eine sehr klasse Geschichte.

Ich würde dazu noch gern wissen, wie (ob) ich einen Seitenvorschub generieren kann?
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Einen String oder Edit-Control zum Drucker senden

13 Jan 2017, 07:10

Die Funktion druckt über DrawTextEx(). Ich glaube nicht, dass dabei Seitenumbrüche erzeugt werden können. Wenn Du aber einen Text auf mehrere Seiten verteilen willst und weißt, wo auf eine neue Seite umgebrochen werden soll, kannst Du ja PrintStr() mit den entsprechenden Textabschnitten mehrfach aufrufen.
User avatar
Gucky_87
Posts: 375
Joined: 03 Jul 2014, 05:09

Re: Einen String oder Edit-Control zum Drucker senden

13 Jan 2017, 07:21

Das ist natürlich korrekt.
Ich denke, ich werd das so auch mal testen.
Dennoch danke fürs darüber Nachdenken.
garry
Posts: 3740
Joined: 22 Dec 2013, 12:50

Re: Einen String oder Edit-Control zum Drucker senden

09 Dec 2018, 14:40

@just me , weiss wieder nicht wie man Funktion benutzt ..
möchte die Parameter im GUI definieren und sollte im Funktion übernommen werden

Code: Select all

PrintStr(Str, fontname:="Lucida Console", fontsize:="11",fontopts:="s11")
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Einen String oder Edit-Control zum Drucker senden

09 Dec 2018, 21:47

Moin garry,

die Schriftgröße wird über den Parameter FontSize übergeben und sollte deshalb nicht nochmals in FontOpts auftauchen. Wenn Du die Variablen für die Parameter über ein GUI füllst, sollte das im Prinzip so aussehen:

Code: Select all

fontname := "Lucida Console" ; z.B. aus Gui, Submit
fontsize := 11 ; z.B. aus Gui, Submit
fontopts := "bold" ; z.B. aus Gui, Submit
PrintStr(Str, fontname, fontsize, fontopts)
garry
Posts: 3740
Joined: 22 Dec 2013, 12:50

Re: Einen String oder Edit-Control zum Drucker senden

10 Dec 2018, 00:54

@just me, vielen Dank , super scripts wie immer ...
musste noch anpassen > If !PrintStr(Str, fontname, fontsize, fontopts)

Code: Select all

fontname := "Lucida Console"
fontsize := 11 
;fontopts := "s11"
fontopts := "Bold"
;.....
Gui, Show, , Print Edit
Return
;-------------------
Print:
ControlGetText, Str, , ahk_id %HED% ; it's important to use ControlGetText if you read the text from an Edit control
If (Str) {
   If !PrintStr(Str, fontname, fontsize, fontopts)
      MsgBox, 0, PrintStr, An error occurred!`nErrorLevel = %ErrorLevel%
}
Return

PrintStr(Str, fontname, fontsize, fontopts){
;...

Return to “Ich brauche Hilfe”

Who is online

Users browsing this forum: Google [Bot] and 34 guests