Simple GDI-Printing library -- SGDIPrint

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
oldbrother
Posts: 273
Joined: 23 Oct 2013, 05:08

Simple GDI-Printing library -- SGDIPrint

29 Sep 2019, 07:11

I got this great library from the old forum long time ago, and used it in many of my scripts.

I saw someone was looking for the way to print from AHK. I tried to find the old link for him, but it seemed lost. Therefore I decided to post it here.

I think we should have a place or a way (a list?) in Autohotkey.com to keep the libraries and functions to help people to easily find them and use them.

This library needs gdip.ahk, you can find it from the forum.

The Library:

Code: Select all

; SGDIPrint-library
; Simple GDI-Printing library for AHK_L (32bit & 64bit compatible)
; by Zed_Gecko
; thanks to: engunneer, Lexikos, closed, controlfreak, just me, fincs, tic
; Requires tics GDI+ Library unless you use bare GDI printing (means printing directly on the printer-DC)
; http://www.autohotkey.com/forum/viewtopic.php?t=32238
; with GDI+ you can either draw directly on the printer (SGDIPrint_printerfriendlyGraphicsFromHDC) or
; draw on a matching bitmap first (SGDIPrint_GetMatchingBitmap) which you copy later to the printer,
; 	which allows to  preview the print and to save it to file 
; 	but it uses more resources & time, and the printing result may differ from "direct"-printing
;---------------------------------------------------------------
; Functions:
; SGDIPrint_GDIPStartup()						Start GDI+
; SGDIPrint_EnumPrinters()						Get List of Printer Names
; SGDIPrint_GetDefaultPrinter()					Get default-Printer Name
; SGDIPrint_GetHDCfromPrinterName()				Get GDI DC from Printer Name
; SGDIPrint_GetHDCfromPrintDlg()				Get GDI DC from user-dialog
; SGDIPrint_GetMatchingBitmap()					Get a GDI+ Bitmap matching to print-out size
; SGDIPrint_DeleteBitmap()						deletes a GDI+ Bitmap
; SGDIPrint_BeginDocument() 					starts the GDI-print-session
; SGDIPrint_printerfriendlyGraphicsFromBitmap()	creates GDI+ graphic 
; SGDIPrint_CopyBitmapToPrinterHDC()			copies a GDI+ Bitmap to a matching printer GDI DC
; SGDIPrint_printerfriendlyGraphicsFromHDC() 	creates GDI+ graphic 
; SGDIPrint_TextFillUpRect()					fills up a rectangle on a GDI+ graphic with text
; SGDIPrint_DeleteGraphics()					deletes GDI+ graphic 
; SGDIPrint_NextPage()							starts new page in the GDI-print-session
; SGDIPrint_EndDocument()						ends the GDI-print-session
; SGDIPrint_AbortDocument()						aborts the GDI-print-session
;
; SGDIPrint_GDIPShutdown()						End GDI+
;-----------
; Global Vars
; SGDIPrint_GetHDCfromPrinterName() and SGDIPrint_GetHDCfromPrintDlg() create the folowing global vars:
; 	SGDIPrint_HDC_Orientation	Page Orientation:  PORTRAIT = 1  LANDSCAPE = 2
; 	SGDIPrint_HDC_Color			Color-Printing.Mode:  B/W = 1  COLOR = 2
; 	SGDIPrint_HDC_Copies		the number of copies you or user selected [integer]
; 	SGDIPrint_HDC_Width 		Width in pixel
; 	SGDIPrint_HDC_Height		Height in pixel
; 	SGDIPrint_HDC_xdpi			X resolution in DPI
; 	SGDIPrint_HDC_ydpi			Y resolution in DPI

;---------------------------------------------------------------
; Uncomment if Gdip.ahk is not in your standard library
#Include, Gdip.ahk

; SGDIPrint_GDIPStartup inits GDI+ or terminates the script when GDI+ is not availiable
; you can call Gdip_Startup directly if you prefer.
SGDIPrint_GDIPStartup()
{
	If !pToken := Gdip_Startup()
	{
		MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
		ExitApp
	}
	return %pToken%
}

; SGDIPrint_EnumPrinters retrieves a LineFeed-delimited List of NAMES of available printers 
; use this or SGDIPrint_GetDefaultPrinter() to get a valid printer-name for SGDIPrint_GetHDCfromPrinterName()
SGDIPrint_EnumPrinters()
{
PRINTER_ENUM_NAME := 0x8
NULL := ""
Level := 4
pcbNeeded := 0
pcReturned := 0
out := DllCall("Winspool.drv\EnumPrinters", "UInt", PRINTER_ENUM_NAME, "Str", NULL, "UInt", Level, "Ptr", 0, "UInt", 0, "UIntP", pcbNeeded, "UIntP", pcReturned)
pcbGranted := VarSetCapacity(pPrinterEnum , pcbNeeded)

out := DllCall("Winspool.drv\EnumPrinters", "UInt", PRINTER_ENUM_NAME, "Str", NULL, "UInt", Level, "Ptr", &pPrinterEnum, "UInt", pcbGranted, "UIntP", pcbNeeded, "UIntP", pcReturned)
loop, %pcReturned%
{
	Offset := (A_Index - 1) * (A_PtrSize * 3)
	pName := StrGet(Numget(pPrinterEnum, 0 + Offset))
	Namelist .= ((A_Index = 1)? "" : "`n") . pName
}
VarSetCapacity(pPrinterEnum, 0)
return %Namelist%
}

; SGDIPrint_GetDefaultPrinter retrieves the NAME of the default printer
; use this or SGDIPrint_EnumPrinters() to get a valid printer-name for SGDIPrint_GetHDCfromPrinterName()
SGDIPrint_GetDefaultPrinter()
{
	Null := ""
	DllCall("winspool.drv\GetDefaultPrinter", "Ptr", NULL, "UintP", nSize)
	if A_IsUnicode
		nSize := VarSetCapacity(gPrinter, nSize*2)
	else
		nSize := VarSetCapacity(gPrinter, nSize)
	DllCall("winspool.drv\GetDefaultPrinter", "Str", gPrinter, "UintP", nSize)
	Defaultprinter := gPrinter
	Return %Defaultprinter%
}

; SGDIPrint_GetHDCfromPrinterName returns a GDI DC-Handle for the printer specified by NAME
;  and sets global Vars SGDIPrint_HDC_Orientation , SGDIPrint_HDC_Color , SGDIPrint_HDC_Copies
;  SGDIPrint_HDC_Width , SGDIPrint_HDC_Height (both in pixel) , SGDIPrint_HDC_xdpi , SGDIPrint_HDC_ydpi (both resolution in DPI)
;  with the optional parameters dmOrientation , dmColor , dmCopies you can change the default values used by the printer:
;	Orientation: PORTRAIT = 1  LANDSCAPE = 2
;	Color: B/W = 1  COLOR = 2
;	Copies: any number of copies you want [integer]
SGDIPrint_GetHDCfromPrinterName(pPrinterName, dmOrientation = 0, dmColor = 0, dmCopies = 0)
{
	global SGDIPrint_HDC_Orientation
	global SGDIPrint_HDC_Color
	global SGDIPrint_HDC_Copies
	global SGDIPrint_HDC_Width 
	global SGDIPrint_HDC_Height
	global SGDIPrint_HDC_xdpi
	global SGDIPrint_HDC_ydpi
	

	MainhWnd := A_ScriptHwnd
	
	NULL := ""
	
	VarSetCapacity(pPrinter , A_PtrSize, 0)
	out := DllCall("Winspool.drv\OpenPrinter", "Ptr", &pPrinterName, "PtrP", pPrinter, "UInt", NULL, "Ptr")

	sizeDevMode := DllCall("Winspool.drv\DocumentProperties", "Ptr", MainhWnd, "Ptr", pPrinter, "Ptr", &pPrinterName, "Ptr", 0, "Ptr", 0, "UInt", 0, "Int")

	VarSetCapacity(pDevModeOutput , sizeDevMode, 0)
	out2 := DllCall("Winspool.drv\DocumentProperties", "Ptr", MainhWnd, "Ptr", pPrinter, "Ptr", &pPrinterName, "Ptr", &pDevModeOutput, "Ptr", 0, "UInt", 2, "Int")
     
	if ((dmOrientation = 1)||(dmOrientation = 2))
		NumPut(dmOrientation, pDevModeOutput, 44, "Short")
	if dmCopies is integer
	{   
		if (dmCopies > 0)
			NumPut(dmCopies, pDevModeOutput, 54, "Short")
	}
	if ((dmColor = 1)||(dmColor = 2))
		NumPut(dmColor, pDevModeOutput, 60, "Short")

	out3 := DllCall("Winspool.drv\DocumentProperties", "Ptr", MainhWnd, "Ptr", pPrinter, "Ptr", &pPrinterName, "Ptr", &pDevModeOutput, "Ptr", &pDevModeOutput, "UInt", 10, "Int")	
	
	dmOrientation := NumGet(pDevModeOutput, 44, "Short")
	dmCopies := NumGet(pDevModeOutput, 54, "Short")
	dmColor := NumGet(pDevModeOutput, 60, "Short")

	DllCall("ClosePrinter", "Ptr", pPrinter)
	
	hDc :=  DllCall("Gdi32.dll\CreateDC", "Str", NULL, "Ptr", &pPrinterName, "Str", NULL, "Ptr", &pDevModeOutput, "Ptr")
	
	SGDIPrint_HDC_Orientation := dmOrientation
	SGDIPrint_HDC_Color := dmColor
	SGDIPrint_HDC_Copies := dmCopies
	
	VarSetCapacity(pDevModeOutput , 0)
	VarSetCapacity(pPrinter , 0)
	
	; Determine the size of the printable area in pixels:
	SGDIPrint_HDC_Width := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",8)
	SGDIPrint_HDC_Height := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",10)

	; Determine the resolution of the printer in pixels per inch:
	SGDIPrint_HDC_xdpi := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",0x58)
	SGDIPrint_HDC_ydpi := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",0x5A)
	
	return %hDc%
}
	

; SGDIPrint_GetHDCfromPrintDlg returns a GDI DC-Handle for the printer selected by the user  in a dialog-window
;  and sets global Vars SGDIPrint_HDC_Orientation , SGDIPrint_HDC_Color , SGDIPrint_HDC_Copies
;  SGDIPrint_HDC_Width , SGDIPrint_HDC_Height (both in pixel) , SGDIPrint_HDC_xdpi , SGDIPrint_HDC_ydpi (both resolution in DPI)
SGDIPrint_GetHDCfromPrintDlg()
{
	global SGDIPrint_HDC_Orientation
	global SGDIPrint_HDC_Color
	global SGDIPrint_HDC_Copies
	global SGDIPrint_HDC_Width 
	global SGDIPrint_HDC_Height
	global SGDIPrint_HDC_xdpi
	global SGDIPrint_HDC_ydpi

	DllCall("LoadLibrary","str","comdlg32.dll")

	pdstructsize :=  (A_PtrSize = 4) ? 66 : 120
	VarSetCapacity(PRINTDIALOG_STRUCT,pdstructsize,0)
	NumPut(pdstructsize,PRINTDIALOG_STRUCT, "UInt")

	PD_HIDEPRINTTOFILE := 0x00100000 
	PD_NOPAGENUMS := 0x00000002 
	PD_NOSELECTION := 0x00000004 
	PD_RETURNDC :=0x100
	PD_USEDEVMODECOPIESANDCOLLATE := 0x40000
	PD_Flags := PD_NOPAGENUMS + PD_NOSELECTION + PD_RETURNDC + PD_HIDEPRINTTOFILE + PD_USEDEVMODECOPIESANDCOLLATE
	NumPut(PD_Flags,PRINTDIALOG_STRUCT, A_PtrSize * 5)

	if !DllCall("comdlg32\PrintDlg","Ptr",&PRINTDIALOG_STRUCT)
		return

	if (hDevNames := NumGet(PRINTDIALOG_STRUCT,A_PtrSize * 3))
		DllCall("GlobalFree","Ptr",hDevNames)
  
	if (hDevModeOutput := NumGet(PRINTDIALOG_STRUCT, A_PtrSize * 2))
	{ 
		pDevModeOutput := DllCall("GlobalLock", "Ptr", hDevModeOutput)
		dmOrientation := NumGet(pDevModeOutput + 0, 44, "Short")
		dmCopies := NumGet(pDevModeOutput + 0, 54, "Short")
		dmColor := NumGet(pDevModeOutput + 0, 60, "Short")
		DllCall("GlobalFree","Ptr",hDevModeOutput)
	}

   ; Get the newly created printer device context.
   if !(hDC := NumGet(PRINTDIALOG_STRUCT,A_PtrSize * 4))
      return

	SGDIPrint_HDC_Orientation := dmOrientation
	SGDIPrint_HDC_Color := dmColor
	SGDIPrint_HDC_Copies := dmCopies
	VarSetCapacity(PRINTDIALOG_STRUCT , 0)
	
	; Determine the size of the printable area in pixels:
	SGDIPrint_HDC_Width := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",8)
	SGDIPrint_HDC_Height := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",10)

	; Determine the resolution of the printer in pixels per inch:
	SGDIPrint_HDC_xdpi := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",0x58)
	SGDIPrint_HDC_ydpi := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",0x5A)
	
	return %hDC%
}


; SGDIPrint_GetMatchingBitmap returns a GDI+ Bitmap matching the current printers page-size with white background
; you need to call SGDIPrint_GetHDCfromPrintDlg or SGDIPrint_GetHDCfromPrinterName first to
; init the global width&height vars
SGDIPrint_GetMatchingBitmap(width = "g", height = "g", color = 0xFFFFFF)
{
	global SGDIPrint_HDC_Width 
	global SGDIPrint_HDC_Height
	if (width = "g")
		width := SGDIPrint_HDC_Width
	if (height = "g")
		height := SGDIPrint_HDC_Height

	pBitmap := Gdip_CreateBitmap(width, height)
;	set background-color (default is white)
	G := Gdip_GraphicsFromImage(pBitmap)
	DllCall("gdiplus.dll\GdipSetPageUnit","Ptr",G,"int",2)
	Gdip_SetSmoothingMode(G, 4)	
	pBrush := Gdip_BrushCreateSolid(0xffffffff)
	Gdip_FillRectangle(G, pBrush, 0, 0, width, height) 
	Gdip_DeleteBrush(pBrush)	
	Gdip_DeleteGraphics(G)
	return %pBitmap%
}



; SGDIPrint_DeleteBitmap deletes a GDI+ Bitmap
SGDIPrint_DeleteBitmap(pBitmap)
{
	Gdip_DisposeImage(pBitmap)
	return
}



; SGDIPrint_BeginDocument starts the GDI-print-session and the first page
;  returns a value > 0 on success
SGDIPrint_BeginDocument(hDC, Document_Name)
{
	VarSetCapacity(DOCUMENTINFO_STRUCT,(A_PtrSize * 4) + 4,0), 
	NumPut((A_PtrSize * 4) + 4, DOCUMENTINFO_STRUCT) 
	NumPut(&Document_Name,DOCUMENTINFO_STRUCT,A_PtrSize)

	if DllCall("Gdi32.dll\StartDoc","Ptr",hDC,"Ptr",&DOCUMENTINFO_STRUCT,"int") > 0
    	out := DllCall("Gdi32.dll\StartPage","Ptr",hDC,"int")
    return %out%
}


; SGDIPrint_printerfriendlyGraphicsFromBitmap returns a GDI+ graphic object with printerfriendly preformatting
SGDIPrint_printerfriendlyGraphicsFromBitmap(pBitmap)
{
	G := Gdip_GraphicsFromImage(pBitmap)
	DllCall("gdiplus.dll\GdipSetPageUnit","Ptr",G,"int",2)
	Gdip_SetSmoothingMode(G, 4)	
	Gdip_SetInterpolationMode(G, 7)
	return %G%
}


; SGDIPrint_CopyBitmapToPrinterHDC copies a GDI+ Bitmap on a matching Printer HDC
SGDIPrint_CopyBitmapToPrinterHDC(pBitmap, hDC)
{
	global SGDIPrint_HDC_Width 
	global SGDIPrint_HDC_Height
	PG := SGDIPrint_printerfriendlyGraphicsFromHDC(hDC)
	Gdip_DrawImage(PG, pBitmap, 0, 0, SGDIPrint_HDC_Width, SGDIPrint_HDC_Height)
	SGDIPrint_DeleteGraphics(PG)
	return
}



; SGDIPrint_printerfriendlyGraphicsFromHDC returns a GDI+ graphic object with printerfriendly preformatting
SGDIPrint_printerfriendlyGraphicsFromHDC(hDC)
{      
	G := Gdip_GraphicsFromHDC(hDC)
	; The default unit of measurement in this case appears to be UnitDisplay.
	; Change it to UnitPixel, or our drawing will be off.
	DllCall("gdiplus.dll\GdipSetPageUnit","Ptr",G,"int",2)
	; Set the smoothing mode to antialias = 4 to make shapes appear smother (only used for vector drawing and filling)
	Gdip_SetSmoothingMode(G, 4)
	; Interpolation mode has been set to HighQualityBicubic = 7
	Gdip_SetInterpolationMode(G, 7)
	return %G%
}



;SGDIPrint_TextFillUpRect(G, Text, xpos, ypos, Width, Height, Font, Style, Align, sizeinpoints, Color, tabstoplist)
;SGDIPrint_TextFillUpRect fills up a rectangle (on GDI+ graphic) with text and returns the part of the text that did not fit.
;supports (configurable) tab-stops and word/page-wraps only on word boundaries
;--parameters--- 
; G = GDI+ Graphic Handle
; Text = text to print
; xpos, ypos, Width, Height = position and size of the rectangle to be filled. default is the whole page
; Font = the name of an existing font. default is Arial
; Style = a number (0-15) defining the font-style. default is regular = 0. add up:
;         Bold := 1 , Italic := 2 , Underline := 4 , Strikeout := 8
; Align = a number (0-2) defining text-alignment. default is left = 0. choose one:
;         Left/Near := 0 , Center := 1 , Right/Far := 2
; sizeinpoints = font size in points(1 Point = ydpi / 72), like your Wordprocessor. default is 12
; Color = color of text in ARGB. default is black = 0xff000000
; tabstoplist = a variable list(delimiter = |) with  tab-stop-offsets in parts-per-hundred (%) of the rect-width
;         Each tab-stop offset, except the first one, is relative to the previous one. 
;         The first tab-stop offset is relative to the rect-boarder
;         default is "10|10|10|10|10|10|10|10|10", 9 TabStops at 10%,20%,...,80%,90% of the rect-width
;---------------------
SGDIPrint_TextFillUpRect(G, Text, xpos = 0, ypos = 0, Width = "full", Height = "full", Font = "Arial", Style = 0, Align = 0, sizeinpoints = 12, Color = 0xff000000, tabstoplist = "10|10|10|10|10|10|10|10|10")
{
	global SGDIPrint_HDC_Width 
	global SGDIPrint_HDC_Height
	global SGDIPrint_HDC_ydpi
	size := Round((SGDIPrint_HDC_ydpi / 72) * sizeinpoints)
	SingleBitPerPixelGridFit := 1
	Rendering := 3

	if !((Style >= 0) && (Style <= 15))
		Style := 0
	if !((Align >= 0) && (Align <= 2))
		Align := 0
	if Color is not number
		Color := 0xff000000
	if xpos is not number
		xpos := 0
	if ypos is not number
		ypos := 0
	if Width is not number
		Width := SGDIPrint_HDC_Width
	if Height is not number
		Height := SGDIPrint_HDC_Height
	if sizeinpoints is not number
		sizeinpoints := 12
		
	Loop, parse, tabstoplist, |
	{
		if (A_Index > 1)
			sanetabstoplist.= "|"
		if !((A_Loopfield > 0) && (A_Loopfield < 100))
			sanetabstoplist.= 10
		else
			sanetabstoplist.= A_Loopfield
		tabstopcount := A_Index
	}
	tabstoplist := sanetabstoplist
		
		
	hFamily := Gdip_FontFamilyCreate(Font)
	hFont := Gdip_FontCreate(hFamily, Size, Style)
	FormatStyle := 0x4000
	hFormat := Gdip_StringFormatCreate(FormatStyle)
	pBrush := Gdip_BrushCreateSolid(Color)
	 
	CreateRectF(RC, xpos, ypos, Width, Height)
	Gdip_SetStringFormatAlign(hFormat, Align)
	Gdip_SetTextRenderingHint(G, Rendering)
	
	
; possible 64bit problem, size of real-number = ptr-size?
	VarSetCapacity(tabstops, tabstopcount * 4)
	Loop, parse, tabstoplist, |
	{
		Sized_Loopfield := A_Loopfield * Width / 100
		NumPut(Sized_Loopfield, tabstops , (A_Index - 1) * 4, "float")		
	}	
	firstTabOffset := 0	
	a := DllCall("gdiplus\GdipSetStringFormatTabStops", "ptr", hFormat, "float", firstTabOffset, "int", tabstopcount, "ptr", &tabstops)
	
	
	VarSetCapacity(RC2, 16)
	DllCall("gdiplus\GdipMeasureString", "ptr", G
		, "wstr", Text, "int", -1, "ptr", hFont, "ptr", &RC, "ptr", hFormat, "ptr", &RC2, "uint*", Chars, "uint*", Lines)

	if (StrLen(Text) > Chars)
	{
		loop
		{
			NewStop := Chars + 2 - A_Index
			if (NewStop <= 0)
				break
			breakchar := SubStr(Text, Newstop , 1)
			if breakchar in %A_Space%,%A_Tab%,`n,`r
				break
		}
	}
	else
		NewStop := Chars
	StringLeft, NewText, Text, NewStop
	StringTrimLeft, Text, Text, NewStop


	E := Gdip_DrawString(G, NewText, hFont, hFormat, pBrush, RC)
	
	
	Gdip_DeleteBrush(pBrush)
	Gdip_DeleteStringFormat(hFormat)   
	Gdip_DeleteFont(hFont)
	Gdip_DeleteFontFamily(hFamily)

	
	return %Text% 
}	




; SGDIPrint_DeleteGraphics deletes the GDI+ graphic object
SGDIPrint_DeleteGraphics(G)
{
	Gdip_DeleteGraphics(G)
	return
}



; SGDIPrint_NextPage creates a new page in the current printer document
SGDIPrint_NextPage(hDC)
{
	DllCall("Gdi32.dll\EndPage","Ptr",hDC,"int")
	DllCall("Gdi32.dll\StartPage","Ptr",hDC,"int")
	return
}  



; SGDIPrint_EndDocument ends the printing session and deletes the DC
SGDIPrint_EndDocument(hDC)
{
	DllCall("Gdi32.dll\EndPage","Ptr",hDC,"int")
	DllCall("Gdi32.dll\EndDoc","Ptr",hDC)
	DeleteDC(hDC)
	return
}


; SGDIPrint_AbortDocument aborts the printing session and deletes the DC
SGDIPrint_AbortDocument(hDC)
{
	DllCall("Gdi32.dll\AbortDoc","Ptr",hDC)
	DeleteDC(hDC)
	return
}



; SGDIPrint_GDIPShutdown ends GDI+
; you can call Gdip_Shutdown directly if you prefer.
SGDIPrint_GDIPShutdown(pToken)
{
	Gdip_Shutdown(pToken)
	return
}


Example 1

Code: Select all

;Simple GDI only Example:
;(let user select printer, print out two pages with few text on each page)

#Include SGDIPrint.ahk
hdc := SGDIPrint_GetHDCfromPrintDlg()

SGDIPrint_BeginDocument(hDC, "print test")

StringToPrint := "Defenestration can be hazardous"
; use GDI text function directly
DllCall("Gdi32.dll\TextOut","uint",hdc,"int",xPos:=SGDIPrint_HDC_xdpi/2.54,"int",yPos:=SGDIPrint_HDC_ydpi/2.54 ,"str",StringToPrint,"int",StrLen(StringToPrint))

SGDIPrint_NextPage(hDC)

StringToPrint := "This is page two"
DllCall("TextOut","uint",hdc,"int",xPos:=SGDIPrint_HDC_xdpi/2.54,"int",yPos:=SGDIPrint_HDC_ydpi/2.54*2 ,"str",StringToPrint,"int",StrLen(StringToPrint))
       
SGDIPrint_EndDocument(hDC)

ExitApp
Example 2

Code: Select all

;Simple GDI+ Example:
;(print one page in landscape-mode on the default printer, draw with GDI+)


#Include SGDIPrint.ahk
pToken := SGDIPrint_GDIPStartup()

pPrinterName := SGDIPrint_GetDefaultPrinter()
hdc := SGDIPrint_GetHDCfromPrinterName(pPrinterName,2,1,1)

 
SGDIPrint_BeginDocument(hDC, "print test")
       
G := SGDIPrint_printerfriendlyGraphicsFromHDC(hDC)

pPen := Gdip_CreatePen(0xffff0000, 3)
;Gdip_DrawEllipse(G, pPen, 50, 50, SGDIPrint_HDC_Width-100, SGDIPrint_HDC_Height-100)
;Gdip_DrawLine(pGraphics, pPen, x1, y1, x2, y2)
Gdip_DrawLine(G, pPen, 50, 500, 1000, 500)
Gdip_DeletePen(pPen)
pPen := Gdip_CreatePen(0x660000ff, 10)
Gdip_DrawRectangle(G, pPen, 50, 50, SGDIPrint_HDC_Width-100, SGDIPrint_HDC_Height-100)
Gdip_DeletePen(pPen)


SGDIPrint_DeleteGraphics(G)
       
SGDIPrint_EndDocument(hDC)

SGDIPrint_GDIPShutdown(pToken)
ExitApp
Example 3 Text printing

Code: Select all

;GDI+ Print-Text Example:
;(fill one page with text, print, then show what could not fit on the page) 

#Include SGDIPrint.ahk
Gui, Add, Edit, w400 h600 WantTab vText
Gui, Add, Button, gprint, Print
Gui, show, , Enter Text with Tab-Stops and Line-Feeds
return

print:
Gui, Submit

pToken := SGDIPrint_GDIPStartup()
pPrinterName := SGDIPrint_GetDefaultPrinter()
hdc := SGDIPrint_GetHDCfromPrinterName(pPrinterName,1,1,1)
SGDIPrint_BeginDocument(hdc, "DocName")                       ;Document Name
G := SGDIPrint_printerfriendlyGraphicsFromHDC(hdc)
 pPen := Gdip_CreatePen(0xffff0000, 3)
 Gdip_DrawLine(G, pPen, 50, 400, 5000,400)
 
 ;SGDIPrint_TextFillUpRect(G, Text, xpos = 0, ypos = 0, Width = "full", Height = "full", Font = "Arial", Style = 0, Align = 0, sizeinpoints = 12, Color = 0xff000000, tabstoplist = "10|10|10|10|10|10|10|10|10")

NewText := SGDIPrint_TextFillUpRect(G, Text, 400, 400, SGDIPrint_HDC_Width - 800, SGDIPrint_HDC_Height - 800)

SGDIPrint_EndDocument(hDC)
SGDIPrint_DeleteGraphics(G)
SGDIPrint_GDIPShutdown(pToken)

;if (StrLen(NewText) > 0)
;   MsgBox, Print-Job started. The following was nor printed:`n%NewText%
;else
 ;  MsgBox, Print-Job started. Everything was printed.
ExitApp
Example 4

Code: Select all

;GDI+ Example with hBitmap:
;(printing on "preview" Bitmap, save to file, then print-out.) 


#Include SGDIPrint.ahk

pToken := SGDIPrint_GDIPStartup()

pPrinterName := SGDIPrint_GetDefaultPrinter()
hdc := SGDIPrint_GetHDCfromPrinterName(pPrinterName,1,1,1)

; - "print" to bitmap -
pBitmap := SGDIPrint_GetMatchingBitmap()
G := SGDIPrint_printerfriendlyGraphicsFromBitmap(pBitmap)

pPen := Gdip_CreatePen(0xffff0000, 3)
Gdip_DrawEllipse(G, pPen, 50, 50, SGDIPrint_HDC_Width-100, SGDIPrint_HDC_Height-100)
Gdip_DeletePen(pPen)
pPen := Gdip_CreatePen(0x660000ff, 10)
Gdip_DrawRectangle(G, pPen, 50, 50, SGDIPrint_HDC_Width-100, SGDIPrint_HDC_Height-100)
Gdip_DeletePen(pPen)
; --

; - save bitmap to file -
Gdip_SaveBitmapToFile(pBitmap, "test.jpg")

; - print out "preview" Bitmap -
SGDIPrint_BeginDocument(hDC, "print test")

SGDIPrint_CopyBitmapToPrinterHDC(pBitmap, hDC)
       
SGDIPrint_EndDocument(hDC)
; --

SGDIPrint_DeleteGraphics(G)

SGDIPrint_DeleteBitmap(pBitmap)

SGDIPrint_GDIPShutdown(pToken)
ExitApp

User avatar
DataLife
Posts: 445
Joined: 29 Sep 2013, 19:52

Re: Simple GDI-Printing library -- SGDIPrint

22 Nov 2019, 17:00

Edit 11-23-19 - See solution at the end of this post.

When I print using this function the 4x6 inch image takes the entire 8.5 x 11 inch sheet of paper.

How can I get it to print the same size as the image 4x6 inch ?
When I print this image from windows default photo viewer it prints properly at 4x6 inches.

I found 2 post on the old forum and read all of the posts and still do not know the answer.

https://autohotkey.com/board/topic/76258-ahk-llibsimple-gdi-printing-library/
https://autohotkey.com/board/topic/88606-sgdiprint-library-reposted-with-permission/


Here is my code...

Code: Select all

#include gdip.ahk
;TempImage.jpg is 490 x 694
;Prints Image TempImage.jpg
pToken        := SGDIPrint_GDIPStartup()
pPrinterName  := SGDIPrint_GetDefaultPrinter()
pBitmap       := Gdip_CreateBitmapFromFile(A_ScriptDir "\TempImage.jpg")

WidthBmp      := Gdip_GetImageWidth(pBitmap)
HeightBmp     := Gdip_GetImageHeight(pBitmap)
 hdc          := SGDIPrint_GetHDCfromPrinterName(pPrinterName,1,1,1)
                 SGDIPrint_BeginDocument(hDC, "print test")
G             := SGDIPrint_printerfriendlyGraphicsFromHDC(hDC)
WidthDC       := DllCall("GetDeviceCaps","uint",hDC,"int",8) 
HeightDC      := DllCall("GetDeviceCaps","uint",hDC,"int",10) 
WidthScale    := WidthDC/WidthBmp
HeightScale   := HeightDC/HeightBmp
If (WidthScale >= HeightScale)
 Scale := HeightScale
Else
 Scale := WidthScale
hBitmap       := Gdip_CreateHBITMAPFromBitmap(pBitmap)
                 Gdip_SetInterpolationMode(G, 7)
                 Gdip_DrawImage(G, pBitmap
                 , (WidthDC-(WidthBmp*Scale))/2, (HeightDC-(HeightBmp*Scale))/2
                 , WidthBmp * Scale , HeightBmp * Scale, 0, 0, WidthBmp, HeightBmp)
                 SGDIPrint_DeleteGraphics(G)
                 SGDIPrint_EndDocument(hDC)
                 SGDIPrint_GDIPShutdown(pToken)
return



SGDIPrint_GDIPStartup()
{
   If !pToken := Gdip_Startup()
   {
   MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
   return
   }
   return %pToken%
}
SGDIPrint_GetDefaultPrinter()
{
   Null := ""
   DllCall("winspool.drv\GetDefaultPrinter", "Ptr", NULL, "UintP", nSize)
   if A_IsUnicode
   nSize := VarSetCapacity(gPrinter, nSize*2)
   else
   nSize := VarSetCapacity(gPrinter, nSize)
   DllCall("winspool.drv\GetDefaultPrinter", "Str", gPrinter, "UintP", nSize)
   Defaultprinter := gPrinter
   Return %Defaultprinter%
}
SGDIPrint_GetHDCfromPrinterName(pPrinterName, dmOrientation = 0, dmColor = 0, dmCopies = 0)
{
   global SGDIPrint_HDC_Orientation
   global SGDIPrint_HDC_Color
   global SGDIPrint_HDC_Copies
   global SGDIPrint_HDC_Width
   global SGDIPrint_HDC_Height
   global SGDIPrint_HDC_xdpi
   global SGDIPrint_HDC_ydpi
   MainhWnd := A_ScriptHwnd
   NULL := ""
   VarSetCapacity(pPrinter , A_PtrSize, 0)
   out := DllCall("Winspool.drv\OpenPrinter", "Ptr", &pPrinterName, "PtrP", pPrinter, "UInt", NULL, "Ptr")
   sizeDevMode := DllCall("Winspool.drv\DocumentProperties", "Ptr", MainhWnd, "Ptr", pPrinter, "Ptr", &pPrinterName, "Ptr", 0, "Ptr", 0, "UInt", 0, "Int")
   VarSetCapacity(pDevModeOutput , sizeDevMode, 0)
   out2 := DllCall("Winspool.drv\DocumentProperties", "Ptr", MainhWnd, "Ptr", pPrinter, "Ptr", &pPrinterName, "Ptr", &pDevModeOutput, "Ptr", 0, "UInt", 2, "Int")
   if ((dmOrientation = 1)||(dmOrientation = 2))
   NumPut(dmOrientation, pDevModeOutput, 44, "Short")
   if dmCopies is integer
   {  
   if (dmCopies > 0)
   NumPut(dmCopies, pDevModeOutput, 54, "Short")
   }
   if ((dmColor = 1)||(dmColor = 2))
   NumPut(dmColor, pDevModeOutput, 60, "Short")
   out3 := DllCall("Winspool.drv\DocumentProperties", "Ptr", MainhWnd, "Ptr", pPrinter, "Ptr", &pPrinterName, "Ptr", &pDevModeOutput, "Ptr", &pDevModeOutput, "UInt", 10, "Int")  
   dmOrientation := NumGet(pDevModeOutput, 44, "Short")
   dmCopies := NumGet(pDevModeOutput, 54, "Short")
   dmColor := NumGet(pDevModeOutput, 60, "Short")
   DllCall("ClosePrinter", "Ptr", pPrinter)
   hDc :=  DllCall("Gdi32.dll\CreateDC", "Str", NULL, "Ptr", &pPrinterName, "Str", NULL, "Ptr", &pDevModeOutput, "Ptr")
   SGDIPrint_HDC_Orientation := dmOrientation
   SGDIPrint_HDC_Color := dmColor
   SGDIPrint_HDC_Copies := dmCopies
   VarSetCapacity(pDevModeOutput , 0)
   VarSetCapacity(pPrinter , 0)
   ; Determine the size of the printable area in pixels:
   SGDIPrint_HDC_Width := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",8)
   SGDIPrint_HDC_Height := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",10)
   ; Determine the resolution of the printer in pixels per inch:
   SGDIPrint_HDC_xdpi := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",0x58)
   SGDIPrint_HDC_ydpi := DllCall("Gdi32.dll\GetDeviceCaps","Ptr",hDC,"int",0x5A)
   return %hDc%
}
SGDIPrint_BeginDocument(hDC, Document_Name)
{
   VarSetCapacity(DOCUMENTINFO_STRUCT,(A_PtrSize * 4) + 4,0),
   NumPut((A_PtrSize * 4) + 4, DOCUMENTINFO_STRUCT)
   NumPut(&Document_Name,DOCUMENTINFO_STRUCT,A_PtrSize)
   if DllCall("Gdi32.dll\StartDoc","Ptr",hDC,"Ptr",&DOCUMENTINFO_STRUCT,"int") > 0
    out := DllCall("Gdi32.dll\StartPage","Ptr",hDC,"int")
 return %out%
}
SGDIPrint_printerfriendlyGraphicsFromHDC(hDC)
{ 
   G := Gdip_GraphicsFromHDC(hDC)
   ; The default unit of measurement in this case appears to be UnitDisplay.
   ; Change it to UnitPixel, or our drawing will be off.
   DllCall("gdiplus.dll\GdipSetPageUnit","Ptr",G,"int",2)
   ; Set the smoothing mode to antialias = 4 to make shapes appear smother (only used for vector drawing and filling)
   Gdip_SetSmoothingMode(G, 4)
   ; Interpolation mode has been set to HighQualityBicubic = 7
   Gdip_SetInterpolationMode(G, 7)
   return %G%
}
SGDIPrint_DeleteGraphics(G)
{
   Gdip_DeleteGraphics(G)
   return
}
SGDIPrint_EndDocument(hDC)
{
   DllCall("Gdi32.dll\EndPage","Ptr",hDC,"int")
   DllCall("Gdi32.dll\EndDoc","Ptr",hDC)
   DeleteDC(hDC)
   return
}
SGDIPrint_GDIPShutdown(pToken)
{
   Gdip_Shutdown(pToken)
   return
}



Edit 11-23-19
I found that this works. As far as I know it does not affect anything other then the final size of the printed image.
Change these lines....

Code: Select all

WidthScale    := WidthDC/WidthBmp
HeightScale   := HeightDC/HeightBmp
to

Code: Select all

WidthScale    := WidthDC/WidthBmp/1.75
HeightScale   := HeightDC/HeightBmp/1.75

This will create a printed image close to 4 x 6 inches
Check out my scripts. (MyIpChanger) (ClipBoard Manager) (SavePictureAs)
All my scripts are tested on Windows 10, AutoHotkey 32 bit Ansi unless otherwise stated.
User avatar
DataLife
Posts: 445
Joined: 29 Sep 2013, 19:52

Re: Simple GDI-Printing library -- SGDIPrint

18 May 2020, 23:40

I was having trouble getting SGDIPrint to obey the dmOrientation parameter with Autohotkey Unicode. I could only get it to work by using Autohotkey Ansi.
The original thread says it works on Ansi and Unicode but the code was removed due to protest. https://autohotkey.com/board/topic/76258-ahk-llibsimple-gdi-printing-library/

It was reposted here but does not mention Ansi or Unicode https://autohotkey.com/board/topic/88606-sgdiprint-library-reposted-with-permission/

I followed the code and I think I know where the change needs to be to made for the dmOrientation to work.

Code: Select all

hdc := SGDIPrint_GetHDCfromPrinterName(pPrinterName,1,1,1)
The second parameter above is used here as the second parameter...

Code: Select all

SGDIPrint_GetHDCfromPrinterName(pPrinterName, dmOrientation = 0, dmColor = 0, dmCopies = 0)
The dmOrientation parameter above is used here and fails with 0

Code: Select all

dmOrientation := NumGet(pDevModeOutput, 44, "Short")
this always returns 0 using Autohotkey Unicode but works properly on Ansi by returning 1 or 2. This NumGet command is used in a couple other places.

I suspect the last parameter "Short" needs to be changed but since I know nothing about converting code between Ansi and Unicode I do not know what the parameter should be for Unicode.

From the help file
One of the following strings (defaults to UPtr if omitted):
UInt, Int, Int64, Short, UShort, Char, UChar, Double, Float, Ptr or UPtr
And there is probably other changes that needs to be made to this DGDIPrint function to make it Ansi and Unicode compatible.
Check out my scripts. (MyIpChanger) (ClipBoard Manager) (SavePictureAs)
All my scripts are tested on Windows 10, AutoHotkey 32 bit Ansi unless otherwise stated.
robodesign
Posts: 934
Joined: 30 Sep 2017, 03:59
Location: Romania
Contact:

Re: Simple GDI-Printing library -- SGDIPrint

19 May 2020, 16:24

:terms:
DataLife wrote:
18 May 2020, 23:40
I was having trouble getting SGDIPrint to obey the dmOrientation parameter with Autohotkey Unicode. I could only get it to work by using Autohotkey Ansi.
The original thread says it works on Ansi and Unicode but the code was removed due to protest. https://autohotkey.com/board/topic/76258-ahk-llibsimple-gdi-printing-library/

It was reposted here but does not mention Ansi or Unicode https://autohotkey.com/board/topic/88606-sgdiprint-library-reposted-with-permission/

I followed the code and I think I know where the change needs to be to made for the dmOrientation to work.

Code: Select all

hdc := SGDIPrint_GetHDCfromPrinterName(pPrinterName,1,1,1)
The second parameter above is used here as the second parameter...

Code: Select all

SGDIPrint_GetHDCfromPrinterName(pPrinterName, dmOrientation = 0, dmColor = 0, dmCopies = 0)
The dmOrientation parameter above is used here and fails with 0

Code: Select all

dmOrientation := NumGet(pDevModeOutput, 44, "Short")
this always returns 0 using Autohotkey Unicode but works properly on Ansi by returning 1 or 2. This NumGet command is used in a couple other places.

I suspect the last parameter "Short" needs to be changed but since I know nothing about converting code between Ansi and Unicode I do not know what the parameter should be for Unicode.

From the help file
One of the following strings (defaults to UPtr if omitted):
UInt, Int, Int64, Short, UShort, Char, UChar, Double, Float, Ptr or UPtr
And there is probably other changes that needs to be made to this DGDIPrint function to make it Ansi and Unicode compatible.
Could you please post the most bug free edition of this little library? Thank you.

I'm interested in making improvements to it. And integrate it in my image viewer.

Best regards, Marius.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
Quick Picto Viewer: GitHub or forum.
AHK GDI+ expanded / compilation library (on GitHub)
My home page.
User avatar
DataLife
Posts: 445
Joined: 29 Sep 2013, 19:52

Re: Simple GDI-Printing library -- SGDIPrint

19 May 2020, 18:09

As far as I know the most recent version is in the first post of this thread.
It works very nicely on Ansi Autohotkey.
Check out my scripts. (MyIpChanger) (ClipBoard Manager) (SavePictureAs)
All my scripts are tested on Windows 10, AutoHotkey 32 bit Ansi unless otherwise stated.
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: Simple GDI-Printing library -- SGDIPrint

20 May 2020, 21:54

It looks like you found a solution, but i think the scaling math you're using was from my post here:
https://www.autohotkey.com/boards/viewtopic.php?p=251433#p251433
which is meant to scale it to a full page... Instead of then dividing by 1.75, it would work more universally (ie if you have a 3x5 image as well, or more precisely a different resolution) if you just skipped the scaling math I was using altogether and you'll get a pixel perfect print. Basically just remove any reference/math done with variables Scale, HeightScale, WidthScale


Edit: ie try changing:

Code: Select all

WidthScale    := WidthDC/WidthBmp
HeightScale   := HeightDC/HeightBmp
If (WidthScale >= HeightScale)
 Scale := HeightScale
Else
 Scale := WidthScale
hBitmap       := Gdip_CreateHBITMAPFromBitmap(pBitmap)
                 Gdip_SetInterpolationMode(G, 7)
                 Gdip_DrawImage(G, pBitmap
                 , (WidthDC-(WidthBmp*Scale))/2, (HeightDC-(HeightBmp*Scale))/2
                 , WidthBmp * Scale , HeightBmp * Scale, 0, 0, WidthBmp, HeightBmp)
                 SGDIPrint_DeleteGraphics(G)
                 SGDIPrint_EndDocument(hDC)
                 SGDIPrint_GDIPShutdown(pToken)
return
to

Code: Select all

hBitmap       := Gdip_CreateHBITMAPFromBitmap(pBitmap)
                 Gdip_SetInterpolationMode(G, 7)
                 Gdip_DrawImage(G, pBitmap
                 , (WidthDC-WidthBmp)/2, (HeightDC-HeightBmp)/2 ;this math is to center on page
                 , WidthBmp , HeightBmp, 0, 0, WidthBmp, HeightBmp)
                 SGDIPrint_DeleteGraphics(G)
                 SGDIPrint_EndDocument(hDC)
                 SGDIPrint_GDIPShutdown(pToken)
return
DataLife wrote:
22 Nov 2019, 17:00
When I print using this function the 4x6 inch image takes the entire 8.5 x 11 inch sheet of paper...
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
User avatar
DataLife
Posts: 445
Joined: 29 Sep 2013, 19:52

Re: Simple GDI-Printing library -- SGDIPrint

21 May 2020, 12:44

@gwarble
thanks for the suggestion on the printed image size. I will try that when I get some free time.

The other issue I am having is the function does not work properly on unicode. It does not obey the dmOrientation parameter.
With Ansi it works properly.

The details of this issue are in my 2nd post on this thread.
Check out my scripts. (MyIpChanger) (ClipBoard Manager) (SavePictureAs)
All my scripts are tested on Windows 10, AutoHotkey 32 bit Ansi unless otherwise stated.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Simple GDI-Printing library -- SGDIPrint

22 May 2020, 05:21

The offsets for the DEVMODE structure used in the script in the OP are for DEVMODEA (ANSI).


For dmOrientation, dmColor, and dmCopies you have to add 32 when using Unicode.
User avatar
DataLife
Posts: 445
Joined: 29 Sep 2013, 19:52

Re: Simple GDI-Printing library -- SGDIPrint

22 May 2020, 07:13

just me wrote:
22 May 2020, 05:21
The offsets for the DEVMODE structure used in the script in the OP are for DEVMODEA (ANSI).


For dmOrientation, dmColor, and dmCopies you have to add 32 when using Unicode.
thanks, I will give that a try and report back.
Check out my scripts. (MyIpChanger) (ClipBoard Manager) (SavePictureAs)
All my scripts are tested on Windows 10, AutoHotkey 32 bit Ansi unless otherwise stated.
User avatar
DataLife
Posts: 445
Joined: 29 Sep 2013, 19:52

Re: Simple GDI-Printing library -- SGDIPrint

22 May 2020, 08:10

just me wrote:
22 May 2020, 05:21
The offsets for the DEVMODE structure used in the script in the OP are for DEVMODEA (ANSI).


For dmOrientation, dmColor, and dmCopies you have to add 32 when using Unicode.
thanks it works. I added 32 to each of these and it works under unicode.

Code: Select all

       
dmOrientation := NumGet(pDevModeOutput + 0, 44, "Short")
dmCopies := NumGet(pDevModeOutput + 0, 54, "Short")
dmColor := NumGet(pDevModeOutput + 0, 60, "Short")
Check out my scripts. (MyIpChanger) (ClipBoard Manager) (SavePictureAs)
All my scripts are tested on Windows 10, AutoHotkey 32 bit Ansi unless otherwise stated.
robodesign
Posts: 934
Joined: 30 Sep 2017, 03:59
Location: Romania
Contact:

Re: Simple GDI-Printing library -- SGDIPrint

03 Jun 2020, 12:17

@DataLife . I am back. And now, i have this library updated for the latest AHK. And i have all functions working properly. It now relies on my GDI+ library edition. . I tested the functions on Windows 10 , using a Canon printer. Right now, I am implementing image printing in my image viewer.

As a side node, i removed all the functions that were «duplicates» of the ones in GDI+ library, eg., Start, Shutdown and so on. It no longer uses global variables, this breaks all the examples from the initial thread post...

Code: Select all

; SGDIPrint-library
; Simple GDI-Printing library for AHK_L (32bit & 64bit compatible)
; found on : https://www.autohotkey.com/boards/viewtopic.php?f=6&t=68403
; by Zed_Gecko
; updated by Marius Șucan on vendredi 5 juin 2020.
;
; this edition relies on GDI+ library compilation
; https://github.com/marius-sucan/AHK-GDIp-Library-Compilation
;
; thanks to: engunneer, Lexikos, closed, controlfreak, just me, fincs, tic
; Requires tics GDI+ Library unless you use bare GDI printing (means printing directly on the printer-DC)
; http://www.autohotkey.com/forum/viewtopic.php?t=32238
; with GDI+ you can either draw directly on the printer (SGDIPrint_GraphicsFromHDC) or
; draw on a matching bitmap first (SGDIPrint_GetMatchingBitmap) which you copy later to the printer,
; which allows to  preview the print and to save it to file 
; but it uses more resources & time, and the printing result may differ from "direct"-printing
;---------------------------------------------------------------
; Functions:
; SGDIPrint_EnumPrinters()            Get List of Printer Names
; SGDIPrint_SetDefaultPrinter()       Sets default printer by name
; SGDIPrint_GetDefaultPrinter()       Get default-Printer Name
; SGDIPrint_GetHDCfromPrinterName()   Get GDI DC based on Printer Name
; SGDIPrint_GetHDCfromPrintDlg()      Get GDI DC from user-dialog
; SGDIPrint_GetMatchingBitmap()       Get a GDI+ Bitmap matching to print-out size
; SGDIPrint_BeginDocument()           starts the GDI-print-session
; SGDIPrint_GraphicsFromBitmap()      creates GDI+ graphic 
; SGDIPrint_CopyBitmapToPrinterHDC()  copies a GDI+ Bitmap to a matching printer GDI DC
; SGDIPrint_GraphicsFromHDC()         creates GDI+ graphic 
; SGDIPrint_TextFillUpRect()          fills up a rectangle on a GDI+ graphic with text
; SGDIPrint_NextPage()                starts new page in the GDI-print-session
; SGDIPrint_EndDocument()             ends the GDI-print-session
; SGDIPrint_AbortDocument()           aborts the GDI-print-session
; SGDIPrint_AbortPrinter()            it deletes a printer's spool file if the printer is configured for spooling.
; SGDIPrint_OpenPrinterProperties() 
; SGDIPrint_ConnectToPrinterDlg()
;-----------
; This edition no longer relies on global Vars.
; SGDIPrint_GetHDCfromPrinterName() and SGDIPrint_GetHDCfromPrintDlg()
; return an object with the following properties:
;  SGDIPrint.HDC_Orientation  Page Orientation:  PORTRAIT = 1  LANDSCAPE = 2
;  SGDIPrint.HDC_Color        Color-Printing.Mode:  B/W = 1  COLOR = 2
;  SGDIPrint.HDC_Copies       the number of copies you or user selected [integer]
;  SGDIPrint.HDC_Width        Width in pixel
;  SGDIPrint.HDC_Height       Height in pixel
;  SGDIPrint.HDC_xdpi         X resolution in DPI
;  SGDIPrint.HDC_ydpi         Y resolution in DPI
;  SGDIPrint.HDC_ptr          The handle of the HDC
;  SGDIPrint.HDC_PrinterName  The selected/given printer name

; SGDIPrint_EnumPrinters retrieves a list of NAMES of available printers 
; use this or SGDIPrint_GetDefaultPrinter() to get a valid printer-name for SGDIPrint_GetHDCfromPrinterName()
; use DELIM to specify the delimiter for printer names
; set returnArray=1 to retrieve details about each printer

SGDIPrint_EnumPrinters(delim:="`n", flags:=0, returnArray:=0) {
; PrinterEnumFlags:
; PRINTER_ENUM_DEFAULT     = 0x00000001
; PRINTER_ENUM_LOCAL       = 0x00000002
; PRINTER_ENUM_CONNECTIONS = 0x00000004
; PRINTER_ENUM_FAVORITE    = 0x00000004
; PRINTER_ENUM_NAME        = 0x00000008
; PRINTER_ENUM_REMOTE      = 0x00000010
; PRINTER_ENUM_SHARED      = 0x00000020
; PRINTER_ENUM_NETWORK     = 0x00000040
; PRINTER_ENUM_EXPAND      = 0x00004000
; PRINTER_ENUM_CONTAINER   = 0x00008000
; PRINTER_ENUM_ICONMASK    = 0x00ff0000
; PRINTER_ENUM_CATEGORY_ALL= 0x02000000
; PRINTER_ENUM_CATEGORY_3D = 0x04000000
; adapted from: https://www.autohotkey.com/boards/viewtopic.php?t=62955
; a printers class by jNizM

  If !flags
     flags := 0x2|0x4

  if !(DllCall("winspool.drv\EnumPrinters", "uint", flags, "ptr", 0, "uint", 2, "ptr", 0, "uint", 0, "uint*", size, "uint*", 0))
  {
    size := VarSetCapacity(buf, size << 1, 0)
    if (DllCall("winspool.drv\EnumPrinters", "uint", flags, "ptr", 0, "uint", 2, "ptr", &buf, "uint", size, "uint*", 0, "uint*", count))
    {
      addr := &buf, PRINTERS := []
      loop % count
      {
        PRINTERS[A_Index, "ServerName"]      := StrGet(NumGet(addr + 0,              "ptr"))
        PRINTERS[A_Index, "PrinterName"]     := StrGet(NumGet(addr + A_PtrSize,      "ptr"))
        PRINTERS[A_Index, "ShareName"]       := StrGet(NumGet(addr + A_PtrSize *  2, "ptr"))
        PRINTERS[A_Index, "PortName"]        := StrGet(NumGet(addr + A_PtrSize *  3, "ptr"))
        PRINTERS[A_Index, "DriverName"]      := StrGet(NumGet(addr + A_PtrSize *  4, "ptr"))
        PRINTERS[A_Index, "Comment"]         := StrGet(NumGet(addr + A_PtrSize *  5, "ptr"))
        PRINTERS[A_Index, "Location"]        := StrGet(NumGet(addr + A_PtrSize *  6, "ptr"))
        ;DevMode                             := NumGet(addr + A_PtrSize *  7,      "ptr")
        ; https://docs.microsoft.com/en-us/windows/desktop/api/Wingdi/ns-wingdi-_devicemodea
        PRINTERS[A_Index, "SepFile"]         := StrGet(NumGet(addr + A_PtrSize *  8, "ptr"))
        PRINTERS[A_Index, "PrintProcessor"]  := StrGet(NumGet(addr + A_PtrSize *  9, "ptr"))
        PRINTERS[A_Index, "Datatpye"]        := StrGet(NumGet(addr + A_PtrSize * 10, "ptr"))
        PRINTERS[A_Index, "Parameters"]      := StrGet(NumGet(addr + A_PtrSize * 11, "ptr"))
        ;SecurityDescriptor                  := NumGet(addr + A_PtrSize * 12,      "ptr")
        ; https://docs.microsoft.com/de-de/windows/desktop/api/winnt/ns-winnt-_security_descriptor
        PRINTERS[A_Index, "Attributes"]      := NumGet(addr + A_PtrSize * 13,      "uint")
        PRINTERS[A_Index, "Priority"]        := NumGet(addr + A_PtrSize * 13 +  4, "uint")
        PRINTERS[A_Index, "DefaultPriority"] := NumGet(addr + A_PtrSize * 13 +  8, "uint")
        PRINTERS[A_Index, "StartTime"]       := NumGet(addr + A_PtrSize * 13 + 12, "uint")
        PRINTERS[A_Index, "UntilTime"]       := NumGet(addr + A_PtrSize * 13 + 16, "uint")
        PRINTERS[A_Index, "Status"]          := NumGet(addr + A_PtrSize * 13 + 20, "uint")
        PRINTERS[A_Index, "Jobs"]            := NumGet(addr + A_PtrSize * 13 + 24, "uint")
        PRINTERS[A_Index, "AveragePPM"]      := NumGet(addr + A_PtrSize * 13 + 28, "uint")
        thisPrinterName := StrGet(NumGet(addr + A_PtrSize, "ptr"))
        If thisPrinterName
           PRINTERSlist .= thisPrinterName delim
        addr += A_PtrSize * 13 + 32
      }

      If (returnArray=1)
         return PRINTERS
      Else
         return Trim(PRINTERSlist, delim)
    }
  }
  return
}

; SGDIPrint_GetDefaultPrinter retrieves the NAME of the default printer
; use this or SGDIPrint_EnumPrinters() to get a valid printer-name for SGDIPrint_GetHDCfromPrinterName()
; if unsucessful, it returns an empty string.
SGDIPrint_GetDefaultPrinter() {
  DllCall("winspool.drv\GetDefaultPrinter", "Ptr", 0, "Uint*", nSize)
  if A_IsUnicode
     nSize := VarSetCapacity(gPrinter, nSize*2)
  else
     nSize := VarSetCapacity(gPrinter, nSize)

  r := DllCall("winspool.drv\GetDefaultPrinter", "Str", gPrinter, "Uint*", nSize)
  If !r
     gPrinter := ""
  Return gPrinter
}

; SGDIPrint_GetHDCfromPrinterName returns a
; SGDIPrint object. On failure, it returns 0.
;
; Parameters:
; Orientation: PORTRAIT = 1  LANDSCAPE = 2
; Color: B/W = 1  COLOR = 2
; Copies: any number of copies you want [integer]

SGDIPrint_GetHDCfromPrinterName(pPrinterName, dmOrientation:=0, dmColor:=0, dmCopies:=0, MainhWnd:=0) {
  SGDIPrint := []
  If !MainhWnd
     MainhWnd := A_ScriptHwnd

  pPrinterName := Trim(pPrinterName)
  VarSetCapacity(pPrinter , A_PtrSize, 0)
  out := DllCall("Winspool.drv\OpenPrinter", "Ptr", &pPrinterName, "Ptr*", pPrinter, "Ptr", 0, "Ptr")
  If !out
     Return 0

  sizeDevMode := DllCall("Winspool.drv\DocumentProperties", "Ptr", MainhWnd, "Ptr", pPrinter, "Ptr", &pPrinterName, "Ptr", 0, "Ptr", 0, "UInt", 0, "Int")
  VarSetCapacity(pDevModeOutput, sizeDevMode, 0)
  out2 := DllCall("Winspool.drv\DocumentProperties", "Ptr", MainhWnd, "Ptr", pPrinter, "Ptr", &pPrinterName, "Ptr", &pDevModeOutput, "Ptr", 0, "UInt", 2, "Int")

  ansiUnicodeOffSet := (A_IsUnicode=1) ? 32 : 0
  if (dmOrientation=1 || dmOrientation=2)
     NumPut(dmOrientation, pDevModeOutput, 44 + ansiUnicodeOffSet, "Short")

  if dmCopies is integer
  {
     if (dmCopies>0)
        NumPut(dmCopies, pDevModeOutput, 54 + ansiUnicodeOffSet, "Short")
  }

  if (dmColor=1 || dmColor=2)
     NumPut(dmColor, pDevModeOutput, 60 + ansiUnicodeOffSet, "Short")

  out3 := DllCall("Winspool.drv\DocumentProperties", "UPtr", MainhWnd, "Ptr", pPrinter, "Ptr", &pPrinterName, "Ptr", &pDevModeOutput, "Ptr", &pDevModeOutput, "UInt", 10, "Int") 
  SGDIPrint.HDC_PrinterName := pPrinterName
  SGDIPrint.HDC_Orientation := NumGet(pDevModeOutput, 44 + ansiUnicodeOffSet, "Short")
  SGDIPrint.HDC_Color := NumGet(pDevModeOutput, 54 + ansiUnicodeOffSet, "Short")
  SGDIPrint.HDC_Copies := NumGet(pDevModeOutput, 60 + ansiUnicodeOffSet , "Short")
  hDC := DllCall("Gdi32.dll\CreateDC", "Ptr", 0, "WStr", pPrinterName, "Ptr", 0, "Ptr", &pDevModeOutput, "UPtr")
  ; DllCall("ClosePrinter", "Ptr", pPrinter)

  VarSetCapacity(pDevModeOutput, 0)
  VarSetCapacity(pPrinter, 0)
  ; Retrieve the size of the printable area in pixels:
  SGDIPrint.HDC_Width  := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", hDC, "UInt", 8)    ; HORZRES
  SGDIPrint.HDC_Height := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", hDC, "UInt", 10)  ; VERTRES

  ; Retrieve the resolution of the printer in pixels/doots per inch:
  SGDIPrint.HDC_xdpi := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", hDC, "UInt",0x58) ; LOGPIXELSX
  SGDIPrint.HDC_ydpi := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", hDC, "UInt", 0x5A) ; LOGPIXELSY

  ; Retrieve paper physical dimensions and non-printable margins (offset). Values in «device units».
  SGDIPrint.HDC_PHYSICALWIDTH   := DllCall("GetDeviceCaps", "Ptr", hDC, "UInt", 0x6E)
  SGDIPrint.HDC_PHYSICALHEIGHT  := DllCall("GetDeviceCaps", "Ptr", hDC, "UInt", 0x6F)
  SGDIPrint.HDC_PHYSICALOFFSETX := DllCall("GetDeviceCaps", "Ptr", hDC, "UInt", 0x70)
  SGDIPrint.HDC_PHYSICALOFFSETY := DllCall("GetDeviceCaps", "Ptr", hDC, "UInt", 0x71)
  SGDIPrint.HDC_ptr := hDC
  return SGDIPrint
}

; SGDIPrint_GetHDCfromPrintDlg returns a
; SGDIPrint object with the properties of the printer selected
; by the user in the Windows standard print dialog window.
; On failure, it returns 0.

SGDIPrint_GetHDCfromPrintDlg(hwndOwner) {
  Static PD_ALLPAGES := 0x00
       , PD_COLLATE := 0x00000010
       , PD_DISABLEPRINTTOFILE := 0x00080000
       , PD_ENABLEPRINTHOOK := 0x00001000
       , PD_ENABLEPRINTTEMPLATE := 0x00004000
       , PD_ENABLEPRINTTEMPLATEHANDLE := 0x00010000
       , PD_ENABLESETUPHOOK := 0x00002000
       , PD_ENABLESETUPTEMPLATE := 0x00008000
       , PD_ENABLESETUPTEMPLATEHANDLE := 0x00020000
       , PD_NOPAGENUMS := 0x00000008
       , PD_NOWARNING := 0x00000080
       , PD_PRINTSETUP := 0x00000040
       , PD_PRINTTOFILE := 0x00000020
       , PD_RETURNDEFAULT := 0x00000400
       , PD_RETURNIC := 0x00000200
       , PD_SHOWHELP := 0x800
       , PD_USEDEVMODECOPIESANDCOLLATE := 0x040000
       , PD_SELECTION := 0x01
       , PD_PAGENUMS := 0x02
       , PD_NOSELECTION := 0x04
       , PD_RETURNDC := 0x0100
       , PD_USEDEVMODECOPIES := 0x040000
       , PD_HIDEPRINTTOFILE := 0x100000
       , PD_NONETWORKBUTTON := 0x200000
       , PD_NOCURRENTPAGE := 0x800000
       , PD_StructSize := (A_PtrSize=8) ? (13 * A_PtrSize) + 16 : 66
       , PD_Flags := PD_NOCURRENTPAGE | PD_NOSELECTION | PD_RETURNDC | PD_HIDEPRINTTOFILE | PD_USEDEVMODECOPIES

  SGDIPrint := []
  VarSetCapacity(PRINTDIALOG_STRUCT, PD_StructSize, 0)
  NumPut(PD_StructSize, PRINTDIALOG_STRUCT, 0, "UInt")
  NumPut(hwndOwner, PRINTDIALOG_STRUCT, A_PtrSize, "UPtr")
  NumPut(PD_Flags, PRINTDIALOG_STRUCT, A_PtrSize * 5, "UInt")

  if !DllCall("comdlg32\PrintDlg","Ptr", &PRINTDIALOG_STRUCT, "UInt")
     return 0

  ansiUnicodeOffSet := (A_IsUnicode=1) ? 32 : 0
  if (hDevNames := NumGet(PRINTDIALOG_STRUCT, A_PtrSize * 3))
     DllCall("GlobalFree", "Ptr", hDevNames)
  
  if (hDevModeOutput := NumGet(PRINTDIALOG_STRUCT, A_PtrSize * 2))
  { 
     pDevModeOutput := DllCall("GlobalLock", "Ptr", hDevModeOutput)
     SGDIPrint.HDC_PrinterName := StrGet(NumGet(hDevModeOutput + 0, 0, "ptr"))
     SGDIPrint.HDC_Orientation := NumGet(pDevModeOutput + 0, 44 + ansiUnicodeOffSet, "Short")
     SGDIPrint.HDC_Color := NumGet(pDevModeOutput + 0, 54 + ansiUnicodeOffSet, "Short")
     SGDIPrint.HDC_Copies := NumGet(pDevModeOutput + 0, 60 + ansiUnicodeOffSet, "Short")
     DllCall("GlobalFree","Ptr",hDevModeOutput)
  }

   ; Get the newly created printer device context.
   if !(hDC := NumGet(PRINTDIALOG_STRUCT, A_PtrSize * 4, "UPtr"))
      return 0

   VarSetCapacity(PRINTDIALOG_STRUCT, 0)
   ; Retrieve the size of the printable area in pixels:
   SGDIPrint.HDC_Width  := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", hDC, "UInt", 8)   ; HORZRES
   SGDIPrint.HDC_Height := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", hDC, "UInt", 10)  ; VERTRES

   ; Retrieve the resolution of the printer in pixels/doots per inch:
   SGDIPrint.HDC_xdpi := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", hDC, "UInt", 0x58) ; LOGPIXELSX
   SGDIPrint.HDC_ydpi := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", hDC, "UInt", 0x5A) ; LOGPIXELSY

   ; Retrieve paper physical dimensions and non-printable margins (offset). Values in «device units».
   SGDIPrint.HDC_PHYSICALWIDTH   := DllCall("GetDeviceCaps", "Ptr", hDC, "UInt", 0x6E)
   SGDIPrint.HDC_PHYSICALHEIGHT  := DllCall("GetDeviceCaps", "Ptr", hDC, "UInt", 0x6F)
   SGDIPrint.HDC_PHYSICALOFFSETX := DllCall("GetDeviceCaps", "Ptr", hDC, "UInt", 0x70)
   SGDIPrint.HDC_PHYSICALOFFSETY := DllCall("GetDeviceCaps", "Ptr", hDC, "UInt", 0x71)
   SGDIPrint.HDC_ptr := hDC
   ; MsgBox, % SGDIPrint.HDC_PrinterName
   return SGDIPrint
}

; SGDIPrint_GetMatchingBitmap returns a GDI+ Bitmap matching the current printers page-size with white background
; you need to call SGDIPrint_GetHDCfromPrintDlg or SGDIPrint_GetHDCfromPrinterName first to
; retrieve width and height values
SGDIPrint_GetMatchingBitmap(width, height, color:="0xFFFFFF") {
  ; set background-color (default is white)
  pBitmap := Gdip_CreateBitmap(width, height)
  G := Gdip_GraphicsFromImage(pBitmap)
  Gdip_SetPageUnit(G, 2)
  Gdip_SetSmoothingMode(G, 4) 
  pBrush := Gdip_BrushCreateSolid(0xffffffff)
  Gdip_FillRectangle(G, pBrush, 0, 0, width, height) 
  Gdip_DeleteBrush(pBrush)  
  Gdip_DeleteGraphics(G)
  return pBitmap
}

; SGDIPrint_BeginDocument starts the GDI-print-session and the first page
; returns a value>0 on success
SGDIPrint_BeginDocument(hDC, Document_Name) {
  VarSetCapacity(DOCUMENTINFO_STRUCT,(A_PtrSize * 4) + 4,0), 
  NumPut((A_PtrSize * 4) + 4, DOCUMENTINFO_STRUCT) 
  NumPut(&Document_Name,DOCUMENTINFO_STRUCT,A_PtrSize)

  r := DllCall("Gdi32.dll\StartDoc","Ptr", hDC, "Ptr", &DOCUMENTINFO_STRUCT, "int")
  if (r>0)
     out := DllCall("Gdi32.dll\StartPage","Ptr",hDC,"int")
 
  return out
}

; SGDIPrint_GraphicsFromBitmap returns a GDI+ graphic object with printerfriendly preformatting
SGDIPrint_GraphicsFromBitmap(pBitmap) {
  G := Gdip_GraphicsFromImage(pBitmap, 7, 4, 2)
  return G
}

; SGDIPrint_CopyBitmapToPrinterHDC copies a GDI+ Bitmap on a matching Printer HDC
SGDIPrint_CopyBitmapToPrinterHDC(pBitmap, hDC, Width, Height) {
  PG := SGDIPrint_GraphicsFromHDC(hDC)
  If PG
  {
     Gdip_DrawImage(PG, pBitmap, 0, 0, Width, Height)
     Gdip_DeleteGraphics(PG)
  }
  return
}

; SGDIPrint_GraphicsFromHDC returns a GDI+ graphic object with printerfriendly preformatting
SGDIPrint_GraphicsFromHDC(hDC) {
  ; The default unit of measurement when creating Graphics from a DC is UnitDisplay.
  ; It must be UnitPixel, or our drawing will be off.
  G := Gdip_GraphicsFromHDC(hDC, "", 7, 4, 2)
  return G
}

; SGDIPrint_NextPage creates a new page in the current printer document
; if return value not 0, then an error occured
SGDIPrint_NextPage(hDC) {
  DllCall("Gdi32.dll\EndPage","Ptr",hDC,"int")
  r := DllCall("Gdi32.dll\StartPage","Ptr",hDC,"int")
  return r
}  

; SGDIPrint_EndDocument ends the printing session and deletes the DC
; if return value not 0, then an error occured
SGDIPrint_EndDocument(hDC) {
  r := DllCall("Gdi32.dll\EndPage","Ptr",hDC,"int")
  DllCall("Gdi32.dll\EndDoc","Ptr",hDC)
  DeleteDC(hDC)
  return r
}

; SGDIPrint_AbortDocument aborts the printing session and deletes the DC
SGDIPrint_AbortDocument(hDC) {
  DllCall("Gdi32.dll\AbortDoc","Ptr",hDC)
  DeleteDC(hDC)
  return
}

SGDIPrint_SetDefaultPrinter(pPrinterName) {
   if !(DllCall("winspool.drv\SetDefaultPrinter", "Str", pPrinterName))
      return 0
   return 1
}

SGDIPrint_OpenPrinterProperties(pPrinterName, hwndParent) {
  out := DllCall("Winspool.drv\OpenPrinter", "Ptr", &pPrinterName, "Ptr*", pPrinter, "Ptr", 0, "Ptr")
  If !out
     Return 0

   if !(DllCall("winspool.drv\PrinterProperties", "Ptr", hwndParent, "Ptr", pPrinter))
      return 0
   return 1
}

SGDIPrint_AbortPrinter(pPrinterName) {
  out := DllCall("Winspool.drv\OpenPrinter", "Ptr", &pPrinterName, "Ptr*", pPrinter, "Ptr", 0, "Ptr")
  If !out
     Return 0

   if !(DllCall("winspool.drv\AbortPrinter", "Ptr", pPrinter))
      return 0
   return 1
}

SGDIPrint_ConnectToPrinterDlg(hwndParent) {
   if !(DllCall("winspool.drv\ConnectToPrinterDlg", "Ptr", hwndParent, "uint", 0))
      return 0
   return 1
}

Best regards, Marius.
Last edited by robodesign on 05 Jun 2020, 06:14, edited 2 times in total.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
Quick Picto Viewer: GitHub or forum.
AHK GDI+ expanded / compilation library (on GitHub)
My home page.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Simple GDI-Printing library -- SGDIPrint

05 Jun 2020, 04:18

robodesign wrote:It now relies on my GDI+ library edition.
Why does it need your GDI+ libraray edition?
robodesign
Posts: 934
Joined: 30 Sep 2017, 03:59
Location: Romania
Contact:

Re: Simple GDI-Printing library -- SGDIPrint

05 Jun 2020, 06:19

just me wrote:
05 Jun 2020, 04:18
robodesign wrote:It now relies on my GDI+ library edition.
Why does it need your GDI+ libraray edition?
It is a good question. The initial library used a DLL call which was not covered in the initial GDI+ lib. It sets page unit to DCs. This function is covered in «my» GDI+ library edition. And it also uses «shorthands» like this: Gdip_GraphicsFromImage(pBitmap, 7, 4, 2) , which sets the Interpolation Mode, Smoothing and Page Unit in one go.

Best regards, Marius.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
Quick Picto Viewer: GitHub or forum.
AHK GDI+ expanded / compilation library (on GitHub)
My home page.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: gwarble, JoeWinograd and 131 guests