Understanding the Synergy Windows printing API
The Synergy Windows printing API enables your application to access the Microsoft Windows printing API and device drivers as well as to use extended printer features in a true Windows environment. To provide features such as file persistence, device independence, embedded graphics, and print preview, the API creates a Windows enhanced metafile and plays records into the metafile. The metafile is then played back to a device (the printer or the screen) to be printed or viewed.
Internally, a metafile is an array of variable-length structures called metafile records, which can store a picture in a device-independent format. The first records in the metafile specify general information such as the resolution of the device on which the picture was created, the dimensions of the picture, and so on. The remaining records, which constitute the bulk of any metafile, correspond to the graphics device interface (GDI) functions required to draw a picture or write out text.
When a metafile is created, it is given a “reference device context.” This means that the size of the page (or other device surface if the device is not a printer), resolution, and device capabilities are defined by that device. Playing back a metafile to a device other than the reference device may or may not work, depending on the level of device-specific support expected by the metafile operations. When Synergy plays back a metafile, the output is scaled to the page size of the destination device. If the page sizes don’t match, the output may be distorted.
You can use a PDF creation application to print from the Synergy Windows printing API to a PDF file, which is more device-independent than a Windows metafile. The user can then view or print the PDF correctly to any device, using a PDF viewer such as Adobe Reader®. There are a number of PDF creators, including some shareware applications, such as CutePDF Writer. |
The device independence of the print files depends on the features you use and whether those features are common among your various printer drivers. You can create a document under one printer, save the file, and print it out to another printer. However, if the file is created using features that are not available to the second printer, it will not print as formatted. But if the file is created using common fonts, point sizes, and attributes (for example, italic or bold), it will print the same on the second printer as on the first.
By default, a metafile is deleted after it is printed or previewed. However, you have the option to save the metafile so it can be replayed to the printer or screen later. (Refer to DWP_PRINT or DWP_DELPRINTER for details.)
The default metafile extension for the Windows printing API is .emf.
We recommend that you don’t you use the Synergy Windows printing API from a service (such as dbssvc), but if you do, you must modify the service to run as a user account that has previously set up the printers that will be used as well as a default printer. In addition, if the Windows printing API is used with a remote LPQUE to xfServer or under xfServerPlus, the ENABLEUSERHIVE registry setting must be set. See Defining environment variables for a specific user for instructions on setting ENABLEUSERHIVE. In Synergy .NET, the Windows printing API requires the main program to have the [STAThread] attribute. The Windows printing API and the underlying Windows COM/print drivers are not thread safe, so you should ensure that only one thread at a time uses the printing API. |
Recommended calling sequence
Each of the Synergy Window printing API functions requires you to pass a report handle. To get this report handle, call %WPR_INFO(report_handle, DWP_GETPRINTER) before you call any other %WPR_ function.
You must .INCLUDE DBLDIR:winprint.def when using the Windows printing API subroutines.
Make sure you initialize your print data structures before using them. If you don’t, unpredictable behavior may occur. For example, you can do a “clear ^i(^m(font_handle))” to set all members to integer 0, and then set the font name, point size, and so forth. |
See the first example under Sample programs for the simplest printing routine you can write to replace an LPQUE statement.
The calls that are commonly used when building a report, in the order they should be called, are as follows. The tables show the subfunctions that can be used after each call.
1. | %WPR_INFO (…, DWP_GETPRINTER)—allocate and return a report handle |
%WPR_SETDEVICE |
%WPR_GETDEVICE |
%WPR_INFO |
%WPR_PRINT |
%WPR_EXECUTE |
---|---|---|---|---|
DWP_COLLATE |
DWP_COLLATE |
DWP_PRINTDLG |
DWP_SETPAGES |
N/A |
2. | %WPR_PRINT(…, DWP_BEGINJOB)—open a document for printing |
%WPR_SETDEVICE |
%WPR_GETDEVICE |
%WPR_INFO |
%WPR_PRINT |
%WPR_EXECUTE |
---|---|---|---|---|
DWP_BACKCOLOR |
DWP_BACKCOLOR |
DWP_GETPEN |
DWP_ADDFILE |
N/A |
3. | %WPR_PRINT(…, DWP_BEGINPAGE)—start a new page for the current document |
%WPR_SETDEVICE |
%WPR_GETDEVICE |
%WPR_INFO |
%WPR_PRINT |
%WPR_EXECUTE |
---|---|---|---|---|
DWP_BACKCOLOR |
DWP_BACKCOLOR |
DWP_GETPEN |
DWP_BITMAP |
N/A |
4. | %WPR_PRINT(…, DWP_ENDPAGE)—end current page for the current document |
%WPR_SETDEVICE |
%WPR_GETDEVICE |
%WPR_INFO |
%WPR_PRINT |
%WPR_EXECUTE |
---|---|---|---|---|
N/A |
DWP_COLLATE |
N/A |
DWP_SETPAGES |
N/A |
5. | %WPR_PRINT(…, DWP_ENDJOB)—close a document |
%WPR_SETDEVICE |
%WPR_GETDEVICE |
%WPR_INFO |
%WPR_PRINT |
%WPR_EXECUTE |
---|---|---|---|---|
N/A |
DWP_COLLATE |
DWP_DELPEN |
DWP_SETPAGES |
DWP_PREVIEW |
6. | %WPR_INFO(…, DWP_DELPRINTER)—delete the report handle |
All associated objects created with GETPRINTER are deleted, so there are no subfunctions to call.
Sample programs
Your Synergy DBL distribution includes a number of example programs that demonstrate how to use the Synergy Windows printing API. We discuss these programs below. See your dbl\examples directory for the files.
The following piece of code is the equivalent of LPQUE(file_to_print), where file_to_print is the name of the file you want to print.
if (%wpr_info(handle, DWP_GETPRINTER)) ;Get the report handle begin xcall wpr_print(handle, DWP_BEGINJOB) ;Create the metafile xcall wpr_print(handle, DWP_ADDFILE, file_to_print) ;Specify the file to print xcall wpr_print(handle, DWP_ENDJOB) ;Close the metafile xcall wpr_execute(handle, DWP_PRINT) ;Begin printing xcall wpr_info(handle, DWP_DELPRINTER) ;Release the printer end
The winrpt1.dbl program shows how to convert an LPQUE report printout to use the Synergy Windows printing API. This program demonstrates the basics of Windows printing using a fixed font. The advantage of this method is that you can easily set the font of an existing report and print it out to a different printer with similar results. If you compile, link, and run this program, your output should look similar to the samples below:
The winrpt2.dbl program shows how to print a report with proportional fonts and an embedded graphic. This program demonstrates the full set of features, including proportional fonts, multiple fonts per page, line drawing, and bitmap file inclusion.
If you compile, link, and run this program, your output should look similar to the samples below:
The program below (wprsampl.dbl) is included in the dbl\examples directory of your Synergy DBL distribution.
; Sample program to get you started with the Synergy DBL Windows ; printing API. Although there are Toolkit calls, they are for messages ; and samples only. The core routines work without UI Toolkit. .include "DBLDIR:winprint.def" .include "WND:tools.def" record print_info lpp ,i4 cpl ,i4 flags ,i4 unused ,a20 printerid ,a80 driver ,a32 record scratch ctr ,i4 file ,a12 ,"c:\meta.smf" shortext ,a60 record myHandles pHandles ,4d2 pPassFail ,4d2 nothing ,i4 ,0 aPrinter ,i4 ,0 bPrinter ,i4 ,0 record textmetricstuff cRow ,i4 ,0 cCol ,i4 ,0 rHeight ,i4 ,0 strLen ,i4 ,0 strHeight ,i4 ,0 m_font_handle ,i4 f_Handle ,i4 proc open(1, o, "tt:") xcall u_start(,,,,,,,10) ;Create a report handle, look at the text metrics, create a ; metafile, view it, and print it. ;Get the printer first...above all else aPrinter = %wpr_info(nothing, DWP_GETPRINTER) writes(1, "And the handle is " + %string(aPrinter)) ;Create the meta file writes(1, "Creating metafile....please wait...") ;Calculate row height for printing rHeight = %wpr_info(aPrinter, DWP_Y_FROM_ROW, 2) ;Start the presses xcall wpr_print(aPrinter, DWP_BEGINJOB, file) xcall wpr_print(aPrinter, DWP_BEGINPAGE) ;Set some static text shortext = " of some text I created with a Synergy/DE metafile." ;And write out a page for ctr from 1 thru 55 begin xcall wpr_print(aPrinter, DWP_WRITEOUT, 0, (rHeight * ctr), & "Line "+ %string(ctr) + shortext) end ;All done, shut it down xcall wpr_print(aPrinter, DWP_ENDPAGE) xcall wpr_print(aPrinter, DWP_ENDJOB) ;Let's look at our handiwork writes(1, "Preview....") xcall wpr_execute(aPrinter, DWP_PREVIEW, file, "Groovy, look at me", TRUE) ;And print it writes(1, "And print it....") xcall wpr_execute(aPrinter, DWP_PRINT) ;All that’s left is to clean up after the party xcall wpr_info(aPrinter, DWP_DELPRINTER) ;Avoid accidental usage aPrinter = 0 xcall u_message("Pretty cool, eh?") ;----------------------------------------------------------------------- ;Here's some text info... ;Calculate and display text metric stuff writes(1, " ") writes(1, "Here's some text metric stuff") bPrinter = %wpr_info(nothing, DWP_GETPRINTER) cCol = %wpr_info(bPrinter, DWP_X_FROM_COL, 10) cRow = %wpr_info(bPrinter, DWP_Y_FROM_ROW, 20) ;Simple way to calculate row height rHeight = %wpr_info(bPrinter, DWP_Y_FROM_ROW, 2) strLen = %wpr_info(bPrinter, DWP_TEXTWIDTH) writes(1, "Col 10 translated to " + %string(cCol) + " pixels") writes(1, "Row 20 translated to " + %string(cRow) + " pixels") writes(1, "Row Height is " + %string(rHeight) + " pixels") strLen = %wpr_info(bPrinter, DWP_TEXTWIDTH) writes(1, "Average char width is " + %string(strLen)) strLen = %wpr_info(bPrinter, DWP_TEXTWIDTH, "m") writes(1, "String length of 'm' is " + %string(strLen)) strLen = %wpr_info(bPrinter, DWP_TEXTWIDTH, "W") writes(1, "String length of 'W' is " + %string(strLen)) strLen = %wpr_info(bPrinter, DWP_TEXTWIDTH, ".") writes(1, "String lenght of '.' is " + %string(strLen)) strLen = %wpr_info(bPrinter, DWP_TEXTWIDTH, "TextString") writes(1, "Width of 'TextString' is " + %string(strLen)) strHeight = %wpr_info(bPrinter, DWP_TEXTHEIGHT) writes(1, "Average string height is " + %string(strHeight)) strHeight = %wpr_info(bPrinter, DWP_TEXTHEIGHT, "L") writes(1, "String Height of 'L' is " + %string(strHeight)) strHeight = %wpr_info(bPrinter, DWP_TEXTHEIGHT, "m") writes(1, "String Height of 'm' is " + %string(strHeight)) strHeight = %wpr_info(bPrinter, DWP_TEXTHEIGHT, "-") writes(1, "String Height of '-' is " + %string(strHeight)) ;Allocate text metric memory handle m_font_handle = %mem_proc(DM_ALLOC, ^size(text_specs)) f_handle = %wpr_info(bPrinter, DWP_TEXTMETRICS, ^m(text_specs, m_font_handle)) ;Display text_specs struc writes(1, " ") xcall u_message("Here are a bunch of text metric numbers") writes(1, "Text metric structure info...") ;Int values writes(1, " Height " + %string(^m(text_specs.height, m_font_handle))) writes(1, " Ascent " + %string(^m(text_specs.ascent, m_font_handle))) writes(1, " Descent " + %string(^m(text_specs.descent, m_font_handle))) writes(1, " IntLeading " + %string(^m(text_specs.intleading, m_font_handle))) writes(1, " ExtLeading " + %string(^m(text_specs.extleading, m_font_handle))) writes(1, " AveWidth " + %string(^m(text_specs.avewidth, m_font_handle))) writes(1, " MaxWidth " + %string(^m(text_specs.maxwidth, m_font_handle))) writes(1, " Weight " + %string(^m(text_specs.weight, m_font_handle))) writes(1, " OverHang " + %string(^m(text_specs.overhang, m_font_handle))) writes(1, " DigitizedX " + %string(^m(text_specs.digitizedX, m_font_handle))) writes(1, " DigitizedY " + %string(^m(text_specs.digitizedY, m_font_handle)));Char values writes(1, " FirstChar " + ^m(text_specs.firstchar, m_font_handle)) writes(1, " LastChar " + ^m(text_specs.lastchar, m_font_handle)) writes(1, " DefChar " + ^m(text_specs.defaultchar, m_font_handle)) writes(1, " BreakChar " + ^m(text_specs.breakchar, m_font_handle)) ;Byte values writes(1, " Italic? " + %string(^m(text_specs.italic, m_font_handle))) writes(1, " UnderLine? " + %string(^m(text_specs.underlined, m_font_handle))) writes(1, " Strikeout? " + %string(^m(text_specs.strikeout, m_font_handle))) writes(1, " Pitch " + %string(^m(text_specs.pitch, m_font_handle))) writes(1, " Family " + %string(^m(text_specs.charset, m_font_handle))) writes(1, " Filler " + ^m(text_specs.text_filler, m_font_handle)) xcall wpr_info(bPrinter, DWP_DELPRINTER) bPrinter = 0 xcall u_message("Well?") ;------------------------------------------------------------------------ ;Let's incorporate Toolkit ;See how I can get U_PRINTSETUP settings into my printer writes(1, " ") writes(1, "Let's play with the toolkit.") writes(1, "Change some setting in the first 4 u_printsetup dialog boxes, ") writes(1, " and see if they hold for the following 4 DWP_PRINTDLGs.") writes(1, " Getting Printer handles: DWP_GETPRINTER combined with" + " u_printsetup") for ctr from 1 thru 4 begin xcall u_printsetup pHandles(ctr) = %wpr_info(nothing, DWP_GETPRINTER) writes(1, " Returned pHandle for loop " + %string(ctr) + & " " + %string(pHandles(ctr))) end writes(1, " Setting based on Printer handles: DWP_PRINTDLG") for ctr from 1 thru 4 begin pPassFail(ctr) = %wpr_info(pHandles(ctr), DWP_PRINTDLG) writes(1, " Returned pHandle for loop " + %string(ctr) + & " " + %string(pHandles(ctr)) + " PassFail is " + & %string(pPassFail(ctr))) end writes(1, " Viewing changed setting: DWP_PRINTDLG") for ctr from 1 thru 4 begin pPassFail(ctr) = %wpr_info(pHandles(ctr), DWP_PRINTDLG) writes(1, " Returned pHandle for loop " + %string(ctr) + & " " + %string(pHandles(ctr)) + " PassFail is " + & %string(pPassFail(ctr))) end writes(1, "Check that u_printquery wasn't hammered") writes(1, "Should be the last u_printsetup, not DWP_PRINTDLG") xcall u_printsetup writes(1, "Deleting Printer handles: DWP_DELPRINTER") for ctr from 1 thru 4 begin pPassFail(ctr) = %wpr_info(pHandles(ctr), DWP_DELPRINTER) writes(1, " Delete pHandle for loop " + %string(ctr) + & " " + %string(pHandles(ctr)) + " PassFail is " + & %string(pPassFail(ctr))) end xcall u_message("What fun can you have?") ;What fun can you have? end
The following is an example of basic print processing that displays the Print dialog and enables the user to select pages. It is included as tprt.dbl in the dbl\examples directory of your Synergy DBL distribution. (Another example in that directory, tprt2.dbl, assumes the default printer and accesses the previewer. The user can change printers from the previewer as long as the application responds by regenerating the metafile when needed.)
main test_print ; .include "DBLDIR:winprint.def" .align .define D_NUMPAGES ,3 ;Just for our example - # of pages to print ;Macro to allow easy access to elements of dlg structure defined below .define M_DLG(e) ^m(dialog_specs.e,dlg) stack record job ,D_HANDLE ;Report handle page ,i4 ;Current page number row ,i4 ;Current row number rowht ,i4 ;Height of a row in pixels pageht ,i4 ;Height of the page in pixels pagewd ,i4 ;Width of the page in pixels nmrows ,i4 ;Number of rows on a page y ,i4 ;Where to print row (vertically) dlg ,a ^size(dialog_specs) ;Structure for DWP_PRINTDLG proc xcall u_start xcall u_update job = %wpr_info(job, DWP_GETPRINTER) ;Get a report handle clear ^i(dlg) ;Initialize structure to all 0s M_DLG(flags) = DWP_PRINTDLG_STYLEPRINT| ;Use Print dialog (not Setup) & DWP_PRINTDLG_SHOWTOFILE| ;Allow "Print to file" & DWP_PRINTDLG_DISABLECURPAGE| ;Don't allow "Current page" & DWP_PRINTDLG_DISABLESELECT| ;Don't allow "Selection" & DWP_PRINTDLG_COLLATE ;Collate if multiple copies M_DLG(pages) = DWP_PRINTDLG_ALLPAGES ;Start with "All" checked M_DLG(copies) = 1 ; and with 1 copy ;Now ask the user where to print, how many copies, what pages, etc. if (%wpr_info(job, DWP_PRINTDLG, dlg) == DWP_PRINTDLG_PRINT) begin ;They said to print it call generate ;Generate the report ;Now we'll let the user preview it first using %wpr_execute(job, DWP_PREVIEW,,, & DWP_PREVIEW_TOOLBAR|DWP_PREVIEW_PRINTDLG) select (DWP_PREVIEW_PRINT), xcall wpr_execute(job, DWP_PRINT) ;They said print it (DWP_PREVIEW_REGEN), begin call generate ;We need to regenerate it xcall wpr_execute(job, DWP_PRINT) ;Then print it end endusing end xcall wpr_info(job, DWP_DELPRINTER) ;Release the report handle xcall u_finish stop ; ;Routine to generate the report generate, xcall wpr_print(job, DWP_BEGINJOB,, 1) ;Start the job rowht = %wpr_info(job, DWP_TEXTHEIGHT, "A") ;Get height of a row xcall wpr_getdevice(job, DWP_PIXELHEIGHT, pageht) ;Page height xcall wpr_getdevice(job, DWP_PIXELWIDTH, pagewd) ;Page width nmrows = pageht / rowht ;Compute how many rows will fit for page from 1 thru D_NUMPAGES ;For each page to print... begin xcall wpr_print(job, DWP_BEGINPAGE) ;Start the page y = 0 ;Start at vertical pixel 0 for row from 1 thru nmrows ;For each row... begin ;Print some text xcall wpr_print(job, DWP_WRITEOUT, 0, y, "This is row " + %string(row)) y += rowht ;Move down a row for next one end ;Just to be fancy, print the page number centered on the bottom ; (see below) xcall print_centered(job, (pageht - rowht), pagewd, "Page " + %string(page)) xcall wpr_print(job, DWP_ENDPAGE) ;Finished with this page end xcall wpr_print(job, DWP_ENDJOB) ;Job is complete return endmain subroutine print_centered ; ; Description: Print text centered horizontally on the page ; ; Arguments: a_job ,n ;Report handle (in, req) a_y ,n ;Vertical pixel coordinate (in, req) a_pagewidth ,n ;Width of the page in pixels (in, req) a_text ,a ;Text to print (in, req) ; ;Note: We do not trim the text for performance reasons. If you need it ; trimmed, trim it before you pass it. .align stack record textwidth ,i4 ;Width of the text in pixels x ,i4 ;Computed horizontal coordinate for ; the text proc textwidth = %wpr_info(a_job, DWP_TEXTWIDTH, a_text) ;Get string width if (textwidth >= a_pagewidth) then ;As wide as the page or wider? x = 0 ;Just start at the left and truncate else ;There is room for centering x = (a_pagewidth - textwidth) / 2 ;Compute left of centered text xcall wpr_print(a_job, DWP_WRITEOUT, x, a_y, a_text) ;And print it xreturn endsubroutine