WinBatch Tech Support Home

Database Search

If you can't find the information using the categories below, post a question over in our WinBatch Tech Support Forum.

TechHome

Printing Information

Can't find the information you are looking for here? Then leave a message over on our WinBatch Tech Support Forum.

Printing direct from Winbatch II


Here is my next stab at printing directly from Winbatch. The example here is not the best as it assumes one font size per page and only works on fixed width fonts. But it should give you an idea of how it all works. Doing anything more than simple printing is not for the faint hearted. As there is a lot that you have to keep track off. But anyway.. this example will print out a bit of text, splitting the lines so that words are never split. Comments please! Iain.

; Printing Direct from Winbatch
; By Iain Dickason (27 July 2003)
; 
; This script uses Win32 API calls to print text documents
GoSub Functions
Text = "Wilson WindowWare makes products that revolve around WinBatch, our award-winning macro scripting language that provides batch automation for every Windows system, "
Text = StrCat(Text,"including Windows or Novell Netware network clients. You can find more about our software right here, and even download it to try out right now. We are sure you will be pleased.",@CRLF)
Text = StrCat(Text,@CRLF,"Automate your PC's with proven results.",@CRLF)
Text = StrCat(Text,@CRLF,"With WinBatch and its WIL language, you get over ten years of proven business automation technology. Get real-world results with our versatile scripting language, ")
Text = StrCat(Text,"24 extender libraries, and our Web-based Tech Support. You get fast results from over 3,500 practical examples. ")
Text = StrCat(Text,"Advanced networking capabilities include support for the full mix of all versions of Windows and Novell Netware. WinBatch can do it.")
; Extender only needed if you use the pGetPrtList function below
AddExtender("WWPRT34i.DLL")
; Get alist of local printers and get the user to select one
printer_list = pGetPrtList(1)
For p = 1 To ItemCount (printer_list,@TAB)
   printer_list = ItemReplace(ItemExtract(1,ItemExtract(p,printer_list,@TAB),"|"),p,printer_list,@TAB)
Next p
PRINTER = AskItemlist("Select Printer",printer_list,@TAB,@SORTED,@SINGLE)
; allow the print job to be aborted if there is an error
IntControl(73,1,0,0,0)
gdi = LoadGDI()
; Create the Print job.  Telling it the printer to use and a name
hdc = Print_CreatePrintJob(gdi,PRINTER,"My test print")
; Find out the size of the print area
print_details = Print_GetPrintSize(gdi,hdc)
top_margin = ItemExtract(4,print_details,@TAB)
left_margin = ItemExtract(3,print_details,@TAB)
page_width = ItemExtract(1,print_details,@TAB)
page_height = ItemExtract(2,print_details,@TAB)
; Create a font to use
font_text = Print_CreateFont(gdi,hdc,"Courier New",14,0,400,@FALSE,@FALSE,@FALSE,0)
; Get the height of the font
font_height = Print_GetFontHeight(gdi,hdc,14)
; Start a new page (1st one)
Print_StartPage(gdi,hdc)
; Select the font created above
Print_SetFont(gdi,hdc,font_text)
; Work out how wide the characters are (only realy works for fixed width fonts) + 1 for space between chars
char_width = Print_GetCharWidths(gdi,hdc,"A","A",@TRUE) + 1
; Work out the number of characters per line
line_length = page_width / char_width
; And lines per page
lines_per_page = page_height / font_height
; Uncomment to do a test print to check line lenght and lines per page.
; Text = Print_TestPrint()
; Set the text to default text alignment (left etc)
Print_SetAlignment(gdi,hdc,0)
; Make @lf the marker for new lines (paragraphs)
Text = StrReplace(Text,@CRLF,@LF)
Text_Len = StrLen(Text)
current_line = 1
; Work out the number of paragraphs
For p = 1 To ItemCount(Text,@LF)+1
   paragraph = ItemExtract(p,Text,@LF)
   If paragraph == "" Then
      current_line = current_line + 1
      Continue
   EndIf
   current_char = 1
   c = 1
   ; This while loop gets the first line of text upto the last space (so words are not split)
   While 1
      c = StrScan (paragraph, " ", c+1, @FWDSCAN)
      If c > line_length Then
         c = StrScan (paragraph, " ", c-1, @BACKSCAN)
         line = StrSub(paragraph,current_char,c - current_char)
         Break
      EndIf
      If c == line_length Then
         line = StrSub(paragraph,current_char,c - current_char)
         Break
      EndIf
      If c == 0 Then
         line = StrSub(paragraph,current_char,line_length)
         c = current_char + line_length
         Break
      EndIf 
   EndWhile
   ; This while loop will print each line for a paragraph
   While line <> ""
      If current_line > lines_per_page Then 
         ; New page needed
         Print_EndPage(gdi,hdc)
         Print_StartPage(gdi,hdc)
         current_line = 1
      EndIf
      line = StrTrim(line)
      ; This will print out the line of text the y is worked out as we know the font height and the line it is to be printed on
      Print_TextOut(gdi,hdc,line,0,(current_line - 1) * font_height)
      current_char = c
      ; This while look gets the next line of text
      While 1
         c = StrScan (paragraph, " ", c+1, @FWDSCAN)
         If c > (line_length + current_char) Then
            c = StrScan (paragraph, " ", c-1, @BACKSCAN)
            line = StrSub(paragraph,current_char,c - current_char)
            Break
         EndIf
         If c == (line_length + current_char) Then
            line = StrSub(paragraph,current_char,c - current_char)
            Break
         EndIf
         If c == 0 Then
            line = StrSub(paragraph,current_char,line_length)
            Break
         EndIf        
      EndWhile
      current_line = current_line + 1
   EndWhile
Next p
; Time to finish the print job
Print_DeleteFont(gdi,font_text)
Print_EndPage(gdi,hdc)
Print_EndPrintJob(gdi,hdc)
Exit
:WBERRORHANDLER
; This will only get called if there was an error
Error = LastError()
ErrorText = IntControl(34,Error,0,0,0)
Print_AbortPrintJob(gdi,hdc)
Message("Error!",StrCat("Error number: ",Error,@CRLF,"Error Text: ",ErrorText,@CRLF,"Line: ",wberrorhandlerline))
Exit
:Functions
#DefineFunction LoadGDI()
   Return DllLoad(StrCat(DirWindows(1),"gdi32.dll"))
#EndFunction
;This expects colour values 0-255 for each
#DefineFunction rgb(red,green,blue)
  Return ((red mod 256)+(green mod 256)*256+(blue mod 256)*256*256)
#EndFunction
;This creates the print job and expects the Printer name (as returned by pGetPrtList(0) or pGetDefPrtInf (1)) and the name to display in print manger
#DefineFunction Print_CreatePrintJob(gdi,PRINTER,JobName)
   hdc=DllCall(gdi,long:"CreateDCA", lpstr:"WINSPOOL", lpstr:PRINTER,lpnull,lpnull)
   bufDocInfo = BinaryAlloc(5*4)
   bufDocName = BinaryAlloc(StrLen(JobName)+1)
   BinaryPokeStr(bufDocName,0,JobName)
   BinaryPoke4(bufDocInfo,0,5*4)
   BinaryPoke4(bufDocInfo,4,IntControl (42, bufDocName, 0, 0, 0))
   BinaryPoke4(bufDocInfo,8,0)
   BinaryPoke4(bufDocInfo,12,0)
   BinaryPoke4(bufDocInfo,16,0)
   printjob = DllCall(gdi,long:"StartDocA",long:hdc, lpbinary:bufDocInfo)
   BinaryFree(bufDocName)
   BinaryFree(bufDocInfo)
   Return hdc
#EndFunction
; This function needs to be called at the end of the print job
#DefineFunction Print_EndPrintJob(gdi,hdc)
   DllCall(gdi,long:"EndDoc", long:hdc)
   DllCall(gdi,long:"DeleteDC", long:hdc)
   DllFree(gdi)
#EndFunction
; Used to start a new page
#DefineFunction Print_StartPage(gdi,hdc)
   Return DllCall(gdi,long:"StartPage", long:hdc)
#EndFunction
; Needs to be called before a new page is started when starting the 2nd + page
#DefineFunction Print_EndPage(gdi,hdc)
   Return DllCall(gdi,long:"EndPage", long:hdc)
#EndFunction
; This can be called to abort the current print job.  If it is not used then you will get a spooling print job left
#DefineFunction Print_AbortPrintJob(gdi,hdc)
   DllCall(gdi,long:"AbortDoc", long:hdc)
   DllCall(gdi,long:"DeleteDC", long:hdc)
   DllFree(gdi)
#EndFunction
; This is used to get details about the print area
#DefineFunction Print_GetPrintSize(gdi,hdc)
   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
   HORZRES = 8 ; Horizontal width in pixels
   VERTRES = 10 ; Vertical height in pixels
   width = DllCall(gdi,long:"GetDeviceCaps", long:hdc,long:HORZRES)
   height = DllCall(gdi,long:"GetDeviceCaps", long:hdc,long:VERTRES)
   offsetx = DllCall(gdi,long:"GetDeviceCaps", long:hdc,long:PHYSICALOFFSETX)
   offsety = DllCall(gdi,long:"GetDeviceCaps", long:hdc,long:PHYSICALOFFSETY)
   Return StrCat(width,@TAB,height,@TAB,offsetx,@TAB,offsety)
#EndFunction
; Creates a new font handle.  You don't need to pass the width unless you want a custom sized font.
#DefineFunction Print_CreateFont(gdi,hdc,name,height,width,weight,italic,underline,strikeout,angle)
   LOGPIXELSY = 90
   LOGPIXELSX = 88
   height = height * DllCall(gdi,long:"GetDeviceCaps", long:hdc,long:LOGPIXELSY) / 72 * -1
   If width <> 0 Then width = width * DllCall(gdi,long:"GetDeviceCaps", long:hdc,long:LOGPIXELSX) / 72 * -1
   Return DllCall(gdi,long:"CreateFontA",long:height,long:width,long:angle*10,long:angle*10,long:weight,long:italic,long:underline,long:strikeout,long:0,long:0,long:0,long:0,long:0,lpstr:name)
#EndFunction
; Needs to be called for each created font to tidy up when finishing
#DefineFunction Print_DeleteFont(gdi,font)
   Return DllCall(gdi,long:"DeleteObject",long:font)
#EndFunction
; Gets the height of the font to work out layout
#DefineFunction Print_GetFontHeight(gdi,hdc,height)
   LOGPIXELSY = 90
   LOGPIXELSX = 88
   Return height * DllCall(gdi,long:"GetDeviceCaps", long:hdc,long:LOGPIXELSY) / 72
#EndFunction
; Makes the font the current font
#DefineFunction Print_SetFont(gdi,hdc,font)
   Return DllCall(gdi,long:"SelectObject",long:hdc,long:font)
#EndFunction
; values for text alignment
TA_NOUPDATECP    = 0 ; The current position is NOT updated after each text output call (DEFAULT)
TA_UPDATECP      = 1 ; The current position IS updated after each text output call
TA_LEFT          = 0 ; The reference point is on the left edge of the bounding box (DEFAULT) 
TA_RIGHT         = 2 ; The reference point is on the right edge of the bounding box
TA_CENTER        = 6 ; The reference point is middle of the bounding box
TA_TOP           = 0 ; The reference point is on the top edge of the bounding box (DEFAULT) 
TA_BOTTOM        = 8 ; The reference point is on the bottom edge of the bounding box
TA_BASELINE      = 24 ; ?? The reference point is on the baseline of the text ??
TA_RTLREADING    = 256 ; Text is laid out right to left
#DefineFunction Print_SetAlignment(gdi,hdc,align)
   Return DllCall(gdi,long:"SetTextAlign",long:hdc,long:align)
#EndFunction
; Changes the text colour.  The colour varable needs to be a RGB value
#DefineFunction Print_SetFontColour(gdi,hdc,colour)
   Return DllCall(gdi,long:"SetTextColor",long:hdc,long:colour)
#EndFunction
; This is used with the TA_UPDATECP flag of SetAlignment.
#DefineFunction Print_SetMovePrintCursor(gdi,hdc,x,y)
   Return DllCall(gdi,long:"MoveToEx",long:hdc,long:x,long:y,lpnull)
#EndFunction
; Prints text at x,y
#DefineFunction Print_TextOut(gdi,hdc,text,x,y)
   Return DllCall(gdi,long:"TextOutA",long:hdc, long:x, long:y, lpstr:text,long:StrLen(text))
#EndFunction
; This should work with tabs in the text. But I've not been able to get it to work.
#DefineFunction Print_TextOutWithTabs(gdi,hdc,text,x,y,tabspace)
   bufTabs = BinaryAlloc(1)
   BinaryPoke(bufTabs,0,tabspace)
;   Return dllcall(strcat(dirwindows(1),"USER32.DLL"),long:"TabbedTextOutA",long:hdc, long:x, long:y, lpstr:text,long:StrLen(text),long:0,lpnull,long:0)
   Return DllCall(StrCat(DirWindows(1),"USER32.DLL"),long:"TabbedTextOutA",long:hdc, long:x, long:y, lpstr:text,long:StrLen(text),long:1,lpbinary:bufTabs,long:0)
#EndFunction
; Gets the width of characters in the current font.
#DefineFunction Print_GetCharWidths(gdi,hdc,firstletter,lastletter,total)
   range = Char2Num(lastletter) - Char2Num(firstletter) + 1
   structsize=3*4 ; 3 ints @4 bytes each
   arraysize=structsize*range ; 100 elements
   abc=BinaryAlloc(arraysize)
   DllCall(gdi,long:"GetCharABCWidthsA",long:hdc, long:Char2Num(firstletter), long:Char2Num(lastletter), lpbinary:abc)
   list = ""
   For a = 0 To range -1
      pre_space = BinaryPeek4(abc,a * 12)
      width = BinaryPeek4(abc,a * 12 + 4)
      post_space = BinaryPeek4(abc,a * 12 + 8)
      If total Then 
         list = ItemInsert(pre_space+width+post_space,-1,list,@TAB)
      Else
         list = ItemInsert(StrCat(pre_space,"|",width,"|",post_space),-1,list,@TAB)
      EndIf
   Next a
   Return list
#EndFunction
#DefineFunction Print_TestPrint()
   Text = ""
   For a = 1 To line_length
      Text = StrCat(Text,StrSub(a,StrLen(a),-1))
   Next a
   For a = 2 To lines_per_page
      Text = StrCat(Text,@CRLF,a)
   Next a
   Return Text
#EndFunction
Return

Article ID:   W16169
File Created: 2004:03:30:15:43:04
Last Updated: 2004:03:30:15:43:04