[SOLVED] Printing to a specific named printer device

Just starting out? Need help? Post your questions and find answers here.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

[SOLVED] Printing to a specific named printer device

Post by Oso »

Hello all, I notice there is an example of printer output in the documentation, but it works on the basis of first asking the user to select a printer — something that is not always possible. For processes that need to generate printer output for any one of several printers and not require user intervention, there doesn't appear to be a function in PureBasic to specify a printer by name, unless there's one that I'm not aware of.

The way I achieved it is to open local shared printers as files and write to the file. It works perfectly for me but I'm not sure if there are other ways though.

Code: Select all

OpenConsole()

If OpenFile(0,"\\localhost\myprintername")
  PrintN("File opened successfully - now printing...")
  WriteStringN(0,"This is a printer test")
  CloseFile(0)
Else
  PrintN("*** Unable to open the printer output as a file ***")
EndIf
Input()
Last edited by Oso on Mon Jan 30, 2023 7:41 pm, edited 1 time in total.
morosh
Enthusiast
Enthusiast
Posts: 293
Joined: Wed Aug 03, 2011 4:52 am
Location: Beirut, Lebanon

Re: Printing to a specific named printer device

Post by morosh »

have a look at:
viewtopic.php?t=80215

HTH
PureBasic: Surprisingly simple, diabolically powerful
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Printing to a specific named printer device

Post by Oso »

morosh wrote: Fri Dec 02, 2022 6:49 pm have a look at: viewtopic.php?t=80215
Thanks for the reply Morosh. Actually I thought quite a lot about your query and I nearly posted a reply to suggest that my solution might be useful, but I came to the conclusion that our requirements were not the same. Microsoft's 'default printer' function is suitable for desktops but not for server processes, where multiple printers are being directed to simultaneously.
hoerbie
Enthusiast
Enthusiast
Posts: 119
Joined: Fri Dec 06, 2013 11:57 am
Location: DE/BY/MUC

Re: Printing to a specific named printer device

Post by hoerbie »

If you need it only for Windows, you can use the winapi functions for opening a printer by its name

Code: Select all

OpenPrinter_(name$, @hPrinter, #Null)
StartDocPrinter_(hPrinter, ...)
StartPagePrinter_(hPrinter)
...
EndPagePrinter_(hPrinter)
EndDocPrinter_(hPrinter)
ClosePrinter_(hPrinter)
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Printing to a specific named printer device

Post by Oso »

hoerbie wrote: Sat Dec 03, 2022 10:45 am If you need it only for Windows, you can use the winapi functions for opening a printer by its name

Code: Select all

OpenPrinter_(name$, @hPrinter, #Null)
StartDocPrinter_(hPrinter, ...)
Thanks, I appreciate the clarification Hoerbie, as this at least suggests it is possible, although I just wish that I could understand how to implement it. I spent some hours during the weekend, but in the absence of a better understanding of how to properly access the Windows APIs, I couldn’t see the way.

I wasn’t clear on whether the print output is intended to be achieved using PB’s printer functions, or via another Windows API — only plain text lines required anyway.
hoerbie
Enthusiast
Enthusiast
Posts: 119
Joined: Fri Dec 06, 2013 11:57 am
Location: DE/BY/MUC

Re: Printing to a specific named printer device

Post by hoerbie »

Hi Oso,

the following procedure is copied together from my really big library I'm using for different printers and a lot of functions, so I'm not sure if it really works now, but it should put you on the right path:

Code: Select all

Procedure printmemoryblock(prtname.s, docname.s, *printdata, printlen.l)
  Protected.l ret
  Protected.i hPrinter
  Protected.s txtyp
  Protected DocInfo.DOC_INFO_1
  If OpenPrinter_(prtname, @hPrinter, #Null)
    txtyp = "RAW"
    DocInfo\pDocName = @docname
    DocInfo\pOutputFile = #Null
    DocInfo\pDatatype = @txtyp
    If StartDocPrinter_(hPrinter, 1, @DocInfo)
      If StartPagePrinter_(hPrinter)
        WritePrinter_(hPrinter, *printdata, printlen, @ret)
        EndPagePrinter_(hPrinter)
      Else
        Debug "PageError"
      EndIf
      EndDocPrinter_(hPrinter)
    Else
      Debug "DocError"
    EndIf
    ClosePrinter_(hPrinter)
  Else
    Debug "OpenError"
  EndIf
EndProcedure
When you call the procedure the first parameter prtname should be the name of the printer shown in the control panel, the second parameter docname can be any name that you want to give the document, this name will be shown while printing in the print spooler. The memory block *printdata should contain the data that you want to print in the coding that the printer needs (for example Ascii, Utf8 or something else), and printlen is the size of the *printdata memory. If you know the specific codes for your printer, you can also integrate esc sequences in the print data, to switch between bold, underline, italic etc.

First the procedure tries to open the printer with its given name, then it tries to start a new document of the raw type with the given document name, and in this document it tries to start a new page, because the windows print spooler uses documents with pages. If all this has worked, you simply write out the memory block to the printer, close the page, close the document and close the printer.
Attention: This raw print works with a lot of (maybe older) printers, but not with every printer!
Normally the windows print spooler uses a graphical printing, where you have to build something like a big graphic of every page and then send this to the printer, instead of the WritePrinter function, but this would get to long here.

Greets, hoerbie
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Printing to a specific named printer device

Post by Oso »

hoerbie wrote: Mon Dec 05, 2022 12:22 pm Hi Oso, the following procedure is copied together from my really big library I'm using for different printers and a lot of functions, so I'm not sure if it really works now, but it should put you on the right path
Many thanks indeed Hoerbie, that works perfectly well. It was necessary for me to change my printer driver to "Generic Text Only", which I often need to do, because of legacy software that sends plain text. Technically, I'm not sure that this should be necessary here, because the "RAW" API mode is supposed to bypass the printer's driver (I was reading this https://learn.microsoft.com/en-us/troub ... to-printer) I found if I retained the Brother laser driver, the print spooler window quickly displayed the print job and then it disappeared without printing. This is the code I used, along with your procedure...

Code: Select all

*printdata = AllocateMemory(100000)
printstring.s + "Test page " + #CRLF$ + #FF$
lenprint = Len(printstring.s)
PokeS(*printdata, printstring.s, lenprint, #PB_Ascii)
printmemoryblock("Generic", "Mytestdocument", *printdata, lenprint)
I was also able to print a PCL job, since I happened to have a file containing PCL graphics I was working on in the past. I used the following successfully...

Code: Select all

*printdata = AllocateMemory(400000)
If ReadFile(0,"j:\pcl.pcl", #PB_Ascii)
  If ReadData(0, *printdata, 331108)
    printmemoryblock("Generic", "Mytestdocument", *printdata, 331108)
  EndIf
EndIf
Again, it was still necessary to use the Generic Text Only driver to make this work. EDIT — Needed to change txtyp = "RAW" to txtyp = "XPS_PASS". It seems this is a change in drivers (see https://h30434.www3.hp.com/t5/LaserJet- ... -p/6569853 )

One question — I was wondering, do you know if your Windows API method can be combined with PureBasic's DrawText function, obviously for page printers that are able to do so? In other words, could you open the print job with the API and then do the below, or do you need to do 'everything' through APIs?

Code: Select all

LoadFont(0, "Arial", 30)
DrawingFont(FontID(0))
DrawText(100, 100, "PureBasic Printer Test")
hoerbie
Enthusiast
Enthusiast
Posts: 119
Joined: Fri Dec 06, 2013 11:57 am
Location: DE/BY/MUC

Re: Printing to a specific named printer device

Post by hoerbie »

Hi Oso,

nice finding with the "generic text" and the pcl file, I've never needed or tried it until now.

I wrote my code for printing on different pos printers like the thermo Epson TM-T88, these printers come with different cables/interfaces like serial (rs-232), lpt, usb (with virtual serial) or lan, my libs handle all this as direct printing to the interface with own drivers.
But sometimes I needed to share a printer between my program and different other programs that only use the normal Windows graphical way, so I searched and tried until I got the raw print with the print spooler working.

Before I switched to PB I programmed in GFA basic, and there I've programmed the graphical way also, but there the Win api was better integrated. Until now I've never tried to mix the Win api and PBs drawing, but I think it should be possible.
A normal Windows print uses something named "device context" to draw on, I'm not sure, but I would think that you can draw with PB on this also, you first need to find out, how to set the device context as drawing base in the StartDrawing(...) or StartVectorDrawing(...) command.
If you find out how to do it, please post it here, there is a chance, that I will need it next year :D

Maybe one of the real PB experts can join in here?

Greets, hoerbie
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Printing to a specific named printer device

Post by Oso »

hoerbie wrote: Mon Dec 05, 2022 7:59 pm Hi Oso, nice finding with the "generic text" and the pcl file, I've never needed or tried it until now.
Hi Hoerbie, thanks for the reply. It seems we have similar backgrounds, because I have also dabbled a lot with various types of printing and also developed a POS system in the distant past. I'm familiar with the receipt printers and the fact that almost everything in POS was either hooked-up through RS232 serial or the Centronics parallel printer port, or a keyboard 'wedge' device.

Yes, it occurred to me that since we're dealing with raw printer data, then a 'real' test should include sending PCL graphics. I've just done a further test with a customer's logo and box-drawn form and they work well too. The thing that concerns me is that the RAW and XPS_PASS modes are not likely to be clear to an end user who's setting up software. Depending on the type of printer driver, they'd need to try both methods until they get it working. Users aren't going to know whether their printer drivers are GDI or XPS.

If I'm honest about it, I was willing to sacrifice the ability to output plain text to a listing printer and only support page-type printers instead, as I'm reasonably happy to just use PureBasic's DrawText(...) function and deal with the line count positions and pagination myself. What I could not use however, was PB's pop-up printer dialogue, in the application. These are automated reports and there's no user involvement.

Regarding the point about using the Windows APIs to draw the output ("device context"), I was unable to find anything that offers a simple function to print a certain size and type of font at a particular place on the page. Every document I found (mostly in https://learn.microsoft.com/en-us/windo ... structures ) refers back to either GDI or XPS printing, then repeating over again, that this 'rendering' is the responsibility of the software application. It was difficult to follow. :?
Post Reply