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, 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
