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.

Tip

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.

Note

If you use the Synergy Windows printing API from a service (such as dbssvc) 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.

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.

Important

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_COLOR
DWP_COPIES
DWP_DUPLEX
DWP_FORMNAME
DWP_ORIENTATION
DWP_PAPERLENGTH
DWP_PAPERSIZE
DWP_PAPERSOURCE
DWP_PAPERWIDTH
DWP_QUALITY
DWP_SCALE
DWP_SPECS

DWP_COLLATE
DWP_COLOR
DWP_COPIES
DWP_DUPLEX
DWP_FORMNAME
DWP_ORIENTATION
DWP_PAPERLENGTH
DWP_PAPERSIZE
DWP_PAPERSOURCE
DWP_PAPERWIDTH
DWP_PIXELHEIGHT
DWP_PIXELWIDTH DWP_PHYSICALHEIGHT
DWP_PHYSICALWIDTH
DWP_QUALITY
DWP_RESOLUTIONX
DWP_RESOLUTIONY
DWP_SCALE
DWP_SPECS
DWP_XOFFSET
DWP_YOFFSET

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_FONT
DWP_TEXTCOLOR

DWP_BACKCOLOR
DWP_COLLATE
DWP_COLOR
DWP_COPIES
DWP_DUPLEX
DWP_FONT
DWP_FORMNAME
DWP_ORIENTATION
DWP_PAPERLENGTH
DWP_PAPERSIZE
DWP_PAPERSOURCE
DWP_PAPERWIDTH
DWP_PIXELHEIGHT
DWP_PIXELWIDTH DWP_PHYSICALHEIGHT
DWP_PHYSICALWIDTH
DWP_QUALITY
DWP_RESOLUTIONX
DWP_RESOLUTIONY
DWP_SCALE
DWP_SPECS
DWP_TEXTCOLOR
DWP_XOFFSET
DWP_YOFFSET

DWP_GETPEN
DWP_LARGESTFONT
DWP_TEXTMETRICS
DWP_X_FROM_COL
DWP_Y_FROM_ROW

DWP_ADDFILE
DWP_SETPAGES

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_FONT
DWP_TEXTCOLOR

DWP_BACKCOLOR
DWP_COLLATE
DWP_COLOR
DWP_COPIES
DWP_DUPLEX
DWP_FONT
DWP_FORMNAME
DWP_ORIENTATION
DWP_PAPERLENGTH
DWP_PAPERSIZE
DWP_PIXELHEIGHT
DWP_PIXELWIDTH DWP_PHYSICALHEIGHT
DWP_PHYSICALWIDTH
DWP_QUALITY
DWP_RESOLUTIONX
DWP_RESOLUTIONY
DWP_SCALE
DWP_SPECS
DWP_TEXTCOLOR
DWP_XOFFSET
DWP_YOFFSET

DWP_GETPEN
DWP_LARGESTFONT
DWP_TEXTMETRICS
DWP_X_FROM_COL
DWP_Y_FROM_ROW

DWP_BITMAP
DWP_LINE
DWP_FILL
DWP_SETPAGES
DWP_WRITEOUT

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
DWP_COLOR
DWP_COPIES
DWP_DUPLEX
DWP_FORMNAME
DWP_ORIENTATION
DWP_PAPERLENGTH
DWP_PAPERSIZE
DWP_PIXELHEIGHT
DWP_PIXELWIDTH DWP_PHYSICALHEIGHT
DWP_PHYSICALWIDTH
DWP_QUALITY
DWP_RESOLUTIONX
DWP_RESOLUTIONY
DWP_SCALE
DWP_SPECS
DWP_XOFFSET
DWP_YOFFSET

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_COLOR
DWP_COPIES
DWP_DUPLEX
DWP_FORMNAME
DWP_ORIENTATION
DWP_PAPERLENGTH
DWP_PAPERSIZE
DWP_PIXELHEIGHT
DWP_PIXELWIDTH DWP_PHYSICALHEIGHT
DWP_PHYSICALWIDTH
DWP_QUALITY
DWP_RESOLUTIONX
DWP_RESOLUTIONY
DWP_SCALE
DWP_SPECS
DWP_XOFFSET
DWP_YOFFSET

DWP_DELPEN
DWP_GETFILENAME

DWP_SETPAGES

DWP_PREVIEW
DWP_PRINT

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