Printing using DrawText_ API call

Windows specific forum
User avatar
DeanH
Enthusiast
Enthusiast
Posts: 281
Joined: Wed May 07, 2008 4:57 am
Location: Adelaide, South Australia
Contact:

Printing using DrawText_ API call

Post by DeanH »

Hi,

I am trying to work up some general purpose printing routines and have hit an odd snag.

I would like to use the Win API function DrawText in order to put text on a page within a specified rectangular area. The function can automatically wrap the text on several lines if the appropriate flags are set. What I have discovered, however, is this function as well as TextOut misbehaves. It looks as if StartDrawing may be the culprit.

Code: Select all

Import ""
  PB_Printer_DC As "_PB_Printer_DC"
EndImport

lprect.RECT

If PrintRequester()
	pdc=PB_Printer_DC
	If StartPrinting("PrintingTest")
		lptemp.l=-MulDiv_(18,GetDeviceCaps_(pdc,#LOGPIXELSY),72.27)
		LoadFont(1,"Arial",lptemp)
;		StartDrawing(PrinterOutput())
;		DrawingFont(FontID(1))
;		FrontColor(0)
;		BackColor(RGB(255,255,255))
;		DrawText(0,0,"This works")
		prnprevfont=SelectObject_(pdc,FontID(1))
		a$="TextOut test"
		TextOut_(pdc,0,400,@a$,Len(a$))
		lprect\left=0
		lprect\top=800
		lprect\right=700
		lprect\bottom=700+400
		b$="This is some long text to print to see if what happens within a rectangular area"
		DrawText_(pdc,@b$,Len(b$),@lprect,#DT_WORDBREAK | #DT_LEFT | #DT_NOPREFIX)
;		StopDrawing()
	EndIf
	StopPrinting()
EndIf
End
If I rem out StartDrawing, DrawingFont, etc then TextOut and DrawText work. If I include StartDrawing, then TextOut prints text at position 0,0 (not 0,400 as indicated) and DrawText_ appears to do nothing.

Is there a workaround?
The big ticket here is not only using DrawText_ to print within a specified area but also to return the height of the area in pixels.

Taa.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8452
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Printing using DrawText_ API call

Post by netmaestro »

iirc when you use this dc customized by Purebasic you must use MoveToEx_() to position the starting point. I could be wrong but this issue sounds familiar from an early post in the bug section when the 2DDrawing library was rewritten. I couldn't find it again or remember who posted it if the FBI waterboarded me, sorry.
BERESHEIT
User avatar
DeanH
Enthusiast
Enthusiast
Posts: 281
Joined: Wed May 07, 2008 4:57 am
Location: Adelaide, South Australia
Contact:

Re: Printing using DrawText_ API call

Post by DeanH »

I added MoveToEx_(pdc,0,400,0) to just before the TextOut_ statement and that worked. I also added MoveToEx_(pdc,0,800,0) to just before the DrawText_ statement but it only printed the first part of the text (up to "text to p") but the rest wasn't printed. If I remark out from StartDrawing( to DrawText( and also StopDrawing(), the printout is produced properly. So not quite there yet.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Printing using DrawText_ API call

Post by srod »

Add a SetTextAlign_(pdc, #TA_LEFT). Worked fine here.

My advice, don't mix api and PB's printing / drawing commands. Use one or the other. You can secure a printing DC using API through other means; either with a print dialog or using CreateDC_() etc.
I may look like a mule, but I'm not a complete ass.
User avatar
DeanH
Enthusiast
Enthusiast
Posts: 281
Joined: Wed May 07, 2008 4:57 am
Location: Adelaide, South Australia
Contact:

Re: Printing using DrawText_ API call

Post by DeanH »

Thank you for that, I'll try it.

Yesterday I eventually came to the same conclusion -- don't mix the PB print functions with the Win ones. I've ended up writing my own Win-based printing procedures except for one that returns the printer DC from either the default printer or the printer dialog. I'm using the PB functions for that but they're the only ones I am now using. What I am doing is producing a set of general-purpose procedures for printing text that allows different fonts and colours and columns where data that is too wide to fit within a column wraps to the next line. I wrote this in a different language years ago and am converting it to PB.
PureLust
Enthusiast
Enthusiast
Posts: 478
Joined: Mon Apr 16, 2007 3:57 am
Location: Germany, NRW

Re: Printing using DrawText_ API call

Post by PureLust »

DeanH wrote:... except for one that returns the printer DC from either the default printer or the printer dialog. I'm using the PB functions for that but they're the only ones I am now using.
Actually I'm working on a Printer-Lib which will give you back a DC for the Printer-Output.
It's Not in a State to publish it right now, but you can already get a Lot of informations about all installed Printers and start printing to them (creating an Output-DC). Because you cannot use this DC for an Output via PB commands, I have to rewrite the drawing-commands bevor it's ready to release.
But - that's the part you've done already by yourselve - so it might be useable for you already.
If you're interested in it, I could send you a Download-Link.
[Dynamic-Dialogs] - create complex GUIs the easy way
[DeFlicker] - easily deflicker your resizeable Windows
[WinFX] - Window Effects (incl. 'click-through' Window)
User avatar
DeanH
Enthusiast
Enthusiast
Posts: 281
Joined: Wed May 07, 2008 4:57 am
Location: Adelaide, South Australia
Contact:

Re: Printing using DrawText_ API call

Post by DeanH »

Sure, I'll have a look. Are you developing it as an addon user library?

I'm not looking at my functions as a "do all". They're specific for what I need, which is mainly printing text in columns. After starting a printjob, there are functions to set the font, colours, margins (which are all measured in tenths of a millimetre and resolution independent and take into account the non-printing area, just use a ruler to work out where things are to be positioned on a page), headers, footers and page numbering. There's a function that sets up the position of each column to be printed, another that treats the headings of columns as a type of header, and one for printing a tab-delimited string as a row set into the columns. There's also a generic LPRINT type command for easy line-by-line printing. I have not added any drawing or images, however.
PureLust
Enthusiast
Enthusiast
Posts: 478
Joined: Mon Apr 16, 2007 3:57 am
Location: Germany, NRW

Re: Printing using DrawText_ API call

Post by PureLust »

DeanH wrote:Sure, I'll have a look.
Actually I'm on my iPad and have no access to my FTP. I'll send you a PM with a link as soon as I'm back in the office tomorrow, ok?
DeanH wrote:Are you developing it as an addon user library
So far I plan it as an Include, because I have no Idea how to use Tailbite.

Relating to the functions you've already done: Sounds great !!! :D

If the Source is not a secret, I would like to have a Look at it. ;)
[Dynamic-Dialogs] - create complex GUIs the easy way
[DeFlicker] - easily deflicker your resizeable Windows
[WinFX] - Window Effects (incl. 'click-through' Window)
User avatar
DeanH
Enthusiast
Enthusiast
Posts: 281
Joined: Wed May 07, 2008 4:57 am
Location: Adelaide, South Australia
Contact:

Re: Printing using DrawText_ API call

Post by DeanH »

I was hoping to made the code available, so yes.
Expect nothing flash, I'm a lazy coder and basically hack stuff until it works. I'm "old school", having been involved with published software since the early 1980's.
Send me a pm with your contact details and I can email a zip file or email me.
PureLust
Enthusiast
Enthusiast
Posts: 478
Joined: Mon Apr 16, 2007 3:57 am
Location: Germany, NRW

Re: Printing using DrawText_ API call

Post by PureLust »

Hi again, ...

as promised, here is the Link to my little ezPrint-Library (in an imho "unreleasable", but nevertheless "working" state).

Attached please find a little Demo-Code to see what's possible so far:

Code: Select all

EnableExplicit

#ezPrint_IncludeDebugHandling	= #True				; see "Lib_ezPrint.pbi" at Line 12 for more information on that constant

XIncludeFile "..\Includes\Lib_ezPrint.pbi"		; File available at: http://www.bmi-online.de/public/PB-Sources/Includes/Lib_ezPrint.pbi


; With the global variable "ezPrint_DebugInfoLevel", you can set the level of debuginfo the ezPrint-Functions will post into the debug-channel

; ezPrint_DebugInfoLevel = #ezPrint_Debug_SimpleInfo
; ezPrint_DebugInfoLevel = #ezPrint_Debug_DetailedInfo
ezPrint_DebugInfoLevel = #ezPrint_Debug_NoDebugInfo


; First, you "could" call "ezPrint_Initialize()", to initialize the ezPrint-Functions
; This is not really necessary, because ezPrint_Initialize() will be called from within the ezPrint-Functions if necessary.

ezPrint_Initialize()	

; After calling ezPrint_Initialize(), there are 2 internal LinkedList ("ezPrintInt_PrinterList()" and "ezPrintInt_DetailedPrinterList()") with a List of all installed Printerdevices.
; See Debug-output for more information.
; Because this list is used internally, be carefull if you use it as well.

; If you want to get these informations in an own list you could do it by using the functions "ezPrint_GetPrinterList()" and "ezPrint_GetDetailedPrinterList()":
; (see Debug-Output and the structures "ezPrintStruc_PrinterSimpleInfo" and "ezPrintStruc_PrinterDetailedInfo" for more Informations on the received Info.)

Define	NumberOfAvailablePrinters.l
NewList AvailablePrinters.ezPrintStruc_PrinterSimpleInfo()
NewList PrinterDetails.ezPrintStruc_PrinterDetailedInfo()

ezPrint_DebugInfoLevel = #ezPrint_Debug_DetailedInfo ; Turn Debug-Output on, to see which informations will be received by "ezPrint_GetPrinterList()" and "ezPrint_GetDetailedPrinterList()"

NumberOfAvailablePrinters = ezPrint_GetPrinterList(AvailablePrinters())
NumberOfAvailablePrinters = ezPrint_GetDetailedPrinterList(PrinterDetails())

ezPrint_DebugInfoLevel = #ezPrint_Debug_NoDebugInfo ; Turn Debug-Output off, because we don't want to get more debug-infos for now


; You can get the Name of the Default-Printer

Debug "" : Debug "----- Result of: ezPrint_GetDefaultPrinter()"
Debug ezPrint_GetDefaultPrinter()

; And you can SELECT a Printer outof the created internal List:
; The "Selected" Printer will be used from most ezPrint-Functions as default, if you do not specify a printer by calling these ezPrint-Functions

Debug "" : Debug "----- Result of: ezPrint_SelectPrinterByName()"
Debug ezPrint_SelectPrinterByName("zebra")				; <========= replace "zebra" with the name (or part of the name) of one of your printers

; ezPrint_SelectPrinterByName() will return "#ezPrint_Error", if it cannot find a Printer in the List with the given Name
;                                           "1", if it has found a Printer with the exact given Name
;                                           "2", if it hase found a Printer, where the given Name is a part of the printers name

; to get the actual selected Printer, you can use: ezPrint_GetSelectedPrinterName()
Debug "" : Debug "----- Result of: ezPrint_GetSelectedPrinterName()"
Debug ezPrint_GetSelectedPrinterName()



; You can "OPEN" a printer by using "ezPrint_OpenPrinter()", and GET information for a single Printer by using "ezPrint_GetPrinterDetails()".
; (SETTING values to a Printer is not supported so far.
;  So, opening a printer just to get Details for the Printer is not realy necessary, if you have used "ezPrint_GetDetailedPrinterList()" already,
;  because you will have the same information for ALL printers already in a LinkedList given to ezPrint_GetDetailedPrinterList())
; See Debug-Output and the structure "ezPrintStruc_PrinterDetailedInfo" for more information on the received Details

Define	PrinterHandle.i
Define	PrinterInfo.ezPrintStruc_PrinterDetailedInfo

PrinterHandle = ezPrint_OpenPrinter(0,"Kyocera")			; <========= replace "Kyocera" with the name (or part of the name) of one of your printers

; ezPrint_OpenPrinter() returns "#ezPrint_Error", if it cannot find a Printer with the given Name
;                               the handle to the Printer, if you've specified the Printer-ID ("0" in the example above)
;                               a PB-like ID, if you passed #PB_Any as the Printer-ID

; If you open a Printer, if will be added to the internal "Opened Printer List": ezPrintInt_OpenedPrinters()
; the stored Details are among other things:
; (see structure "ezPrintStruc_PrinterDetailedInfo" for more Details)

If PrinterHandle
	Debug "" : 	Debug "----- Opened Printer '" + ezPrintInt_OpenedPrinters()\Info\Name + "' by 'ezPrint_OpenPrinter()'"
	Debug "Assigned 'PB-like' Printer-ID: "+Str(ezPrintInt_OpenedPrinters()\PrinterID)
	Debug "API-Handle to this Printer: "+Str(ezPrintInt_OpenedPrinters()\hPrinter)
	
	; You can get the Status of an opened Printer by using "ezPrint_GetPrinterStatus()"
	; See "http://msdn.microsoft.com/en-us/library/dd162845(VS.85).aspx" or the #Printer_Status_xxx Constants for more Information on the possible Printer states
	
	Debug "Printer status: "+Str(ezPrint_GetPrinterStatus()) : Debug ""
	
	; You can get/refresh the details of an opened Printer by using "ezPrint_GetPrinterDetails(@PrinterInfo)"
	; See Debug-Output and structure "ezPrintStruc_PrinterDetailedInfo" for more information on the received informations
	
	ezPrint_DebugInfoLevel = #ezPrint_Debug_DetailedInfo ; Turn Debug-Output on, to see which informations will be received by "ezPrint_GetPrinterDetails()"
	
	ezPrint_GetPrinterDetails(@PrinterInfo)
	Debug "" : Debug "Printerdetails of '" + PrinterInfo\PrinterName + "'  ->  PaperWidth: " + StrF(PrinterInfo\dmPaperWidth / 100,2) + "mm,  PaperLength: " + StrF(PrinterInfo\dmPaperLength / 100,2) + "mm" : Debug ""
	
	ezPrint_DebugInfoLevel = #ezPrint_Debug_NoDebugInfo ; Turn Debug-Output off, because we don't want to get more debug-infos for now
	
	; Use "ezPrint_ClosePrinter()" to close an previously opened printer
	
	ezPrint_ClosePrinter()
	
EndIf


; Further you can get a List of all bins, supported by a specific printer:

ezPrint_DebugInfoLevel = #ezPrint_Debug_DetailedInfo ; Turn Debug-Output on, to see the List of Bins received by "ezPrint_GetBinList()"
	
NewList	BinList.ezPrintStruc_BinInfo()

ezPrint_GetBinList(BinList(), #ezPrint_SystemDefaultPrinter)		; Get the Bin-List of the System-Default-Printer

ezPrint_DebugInfoLevel = #ezPrint_Debug_NoDebugInfo ; Turn Debug-Output off, because we don't want to get more debug-infos for now


;=======================================================================================================

; Finally we start printing, by using "ezPrint_StartPrintJob()" to create a Printjob
; ezPrint_StartPrintJob() will give back a PB-like ID.
; Because you can initialize more than one PrintJobs at a time with ezPrint, you can use this ID to get the related DC by using "ezPrint_GetPrintJobOutputDC(PrintJobID)".

; ===== Note =====
; If you do not need all the informations you could get trough the ezPrint-Functions introduced above,"ezPrint_StartPrintJob()" is the only ezPrint-Function you need
; to start a printout. Further you may need "ezPrint_GetPrintJobOutputDC()" to get the Output-DC and "ezPrint_StopPrintJob()" to close the PrintJob and start printing.

Define PrintJobID.l
Define PrintJobDC.i

PrintJobID = ezPrint_StartPrintJob()		; Without a specified PrintJob- and Printername, ezPrint_StartPrintJob() uses the default PrintJob-Name and the last selected printer.

If PrintJobID <> #ezPrint_Error
	
	PrintJobDC = ezPrint_GetPrintJobOutputDC(PrintJobID)		; Without a specified PrintJobID, ezPrint_GetPrintJobOutputDC() will return the DC of the last selected/initialized PrintJob.
	
	Debug "" : Debug "----- PrintJob started For Printer: " + ezPrintInt_StartedPrintJobs()\PrinterName
	Debug "ID of the PrintJob: "+Str(PrintJobID)
	Debug "DC of the PrintJob: "+Str(PrintJobDC)
	Debug "Name of the initialized PrintJob: " + ezPrintInt_StartedPrintJobs()\PrintJobName
	Debug "Driver used for this PrintJob: " + ezPrintInt_StartedPrintJobs()\PrinterDriver
	
	
	
	Define Text.s = "This is a Test-Printout, using the ezPrint_()-Library" 

 	TextOut_(ezPrint_GetPrintJobOutputDC(), 300, 100, Text, Len(Text)) 
 	
	ezPrint_StopPrintJob()
	
	Debug "" : Debug "PrintJob closed by 'ezPrint_StopPrintJob()'"
	Debug "Printing will start immediately."
	
EndIf
Most of the Lib will be rewritten (to get things cleaned up) and some Functions has to be added to be able to "SET" some Printer-Settings.
Nevertheless I hope that it will be useful to you (or someone else) in some kind - even though it's not cleaned up an finished yet. ;)

Greetz from Germany,
PL.

PS: The ezPrint-Lib will XInclude the file: "Proc_GetLastErrorInfo.pbi"
You will find the Download-Link to that right behind the XInclude-Command within the ezPrint-Source at line 25.
[Dynamic-Dialogs] - create complex GUIs the easy way
[DeFlicker] - easily deflicker your resizeable Windows
[WinFX] - Window Effects (incl. 'click-through' Window)
User avatar
DeanH
Enthusiast
Enthusiast
Posts: 281
Joined: Wed May 07, 2008 4:57 am
Location: Adelaide, South Australia
Contact:

Re: Printing using DrawText_ API call

Post by DeanH »

Thanks for that. I'll download it when I get a chance.
In the meantime, here are some very basic procedures that provide a primitive simple printout. They are all based on the PureBasic functions, not on the WinAPI functions. The idea was to get something like a basic LPRINT function working.

Code: Select all

;Very simple printout functions by Dean Hodgson

;Required global variables
Global prnactive,lpxpixels,lpypixels

;set up the printing font
;uses ID 199 and changes the font for that ID as needed
Procedure PRNSETFONT(fontname$,fontsize=10,bold=0,underline=0,italic=0)
	If prnactive=#False
		ProcedureReturn
	EndIf
	;72.27 is normal but produces a "too large font". 96 seems to agree with what Word generates
        ;cheating as this is a WinAPI function used to scale the font size based on the printer dpi
	lpsize=-MulDiv_(fontsize,GetDeviceCaps_(pdc,#LOGPIXELSY),96)
	StopDrawing()
	If IsFont(199)
		FreeFont(199)
	EndIf
	lpfontstyle=0
	If bold=#True : lpfontstyle=lpfontstyle | #PB_Font_Bold : EndIf
	If italic=#True : lpfontstyle=lpfontstyle | #PB_Font_Italic : EndIf
	If underline=#True : lpfontstyle=lpfontstyle | #PB_Font_Underline : EndIf
	LoadFont(199,fontname$,lpsize,lpfontstyle)
	StartDrawing(PrinterOutput())
	DrawingFont(FontID(199))
EndProcedure

;set colour for printing
Procedure PRNCOLOR(lpcolour,bkcolor=-1)
	If prnactive=#True
		If bkcolor=-1
			bkcolor=RGB(255,255,255)
		EndIf
		BackColor(bkcolor)
		FrontColor(lpcolour)
	EndIf
EndProcedure

;make a guess at the DPI based on the page width
;dpi ~ pagewidth / 8
Procedure.l PRNDPI()
	dpi=0
	If prnactive=#True
		w=PrinterPageWidth()
		If w>1400 And w<1500 : dpi=180 : EndIf
		If w>2300 And w<2400 : dpi=300 : EndIf
		If w>2800 And w<2900 : dpi=360 : EndIf
		If w>4600 And w<4800 : dpi=600 : EndIf
		If w>5000 And w<5900 : dpi=720 : EndIf
		If w>9000 And w<10000 : dpi=1200 : EndIf
		If w>18000 And w<20000 : dpi=2400 : EndIf
	EndIf
	ProcedureReturn dpi
EndProcedure

;begin printjob
;set paramter to 0 to use default printer otherwise requester is displayed
;returns non 0 if printing can proceed
Procedure.l BEGINPRINTING(usedialog=1)
	If usedialog
		result=PrintRequester()
	Else
		result=DefaultPrinter()
	EndIf
	If result=0
		ProcedureReturn result
	EndIf
	lptemp=Random(8999)+1000
	lpfilename$=Str(lptemp)+".TMP"
	n=StartPrinting(lpfilename$)
	If n=0
		ProcedureReturn 0
	EndIf
	StartDrawing(PrinterOutput())
	prnactive=#True
	PRNSETFONT("Courier New",10)
	PRNCOLOR(0)
	lpxpixels=0
	lpypixels=0
	ProcedureReturn result
EndProcedure

;Finish printing
Procedure ENDPRINTING()
	If prnactive=#True
		StopDrawing()
		StopPrinting()
		If IsFont(199)
			FreeFont(199)
		EndIf
	EndIf
	prnactive=#False
EndProcedure

;end current page, get next page ready
Procedure PRNPAGE()
	If prnactive=#True
		NewPrinterPage()
		lpypixels=0
	EndIf
EndProcedure

;prints one line with automatic line and page feed
Procedure PRNPRINT(prns$)
	If prnactive=#False
            ProcedureReturn
	EndIf
	If Len(prns$)=0 : prns$=" " : EndIf
	lpht=TextHeight(prns$)
	lptemp=lpypixels+lpht
	lpvertres=PrinterPageHeight()
	If lptemp>lpvertres
		NewPrinterPage()
		lpypixels=0
	EndIf
	DrawText(lpxpixels,lpypixels,prns$)
	lpypixels=lpypixels+lpht
EndProcedure

;locate printing cursor based on millimeters
;convert mm to pixel coordinates
;page size is assumed to be A4 (21 x 29.7cm)
;position is approxiamte and does not take into account margins and non printing areas
Procedure PRNLOCATE(x,y)
	If x<0 : x=0 : EndIf
	If y<0 : y=0 : EndIf
	lpxpixels=x*PrinterPageWidth()/210
	lpypixels=y*PrinterPageHeight()/297
EndProcedure

;return the current pixel position of the print cursor
Procedure.l PRNXPOS()
	ProcedureReturn lpxpixels
EndProcedure

Procedure.l PRNYPOS()
	ProcedureReturn lpypixels
EndProcedure

;------------------------------------------------------------------------

;test it printing 100 rows over two pages
BEGINPRINTING()
For i=1 To 100
	PRNPRINT(Str(i))
Next
ENDPRINTING()
End

Post Reply