home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 14 Text / 14-Text.zip / SEMINAR1.ZIP / SEMINAR1.DOC
Text File  |  1991-11-27  |  23KB  |  501 lines

  1.                    Printing from Within an Application
  2.                    -----------------------------------
  3.  
  4.                                Written by:
  5.                       Larry Salomon, Jr. (aka 'Q')
  6.                        OS/2 Applications and Tools
  7.                      IBM T.J. Watson Research Center
  8.                           Yorktown Heights, NY
  9.  
  10.                          (LARRYS@YKTVMV.BITNET)
  11.                          (larrys@watson.ibm.com)
  12.                      (larrys@ibmman.watson.ibm.com)
  13.                      (larrys@ibmman2.watson.ibm.com)
  14.  
  15. This seminar may be distributed in any form, providing that no
  16. modifications are made to the text, unless done so by the author.
  17.  
  18. This, and other seminars, can be received via anonymous ftp from the
  19. following sites:
  20.  
  21.         Site                    IP Address      Directory
  22.         ----                    ----------      ---------
  23.         cs.unc.edu              129.109.136.138 /pub/os2.seminars
  24.         network.ucsd.edu        128.54.16.3     /pub/os2.seminars
  25.  
  26. Abstract:  one of the most-common functions desired from an application
  27. is the ability to produce some form of hardcopy of the work done with
  28. that application.  Yet coding to print under OS/2 is one of the hardest
  29. things to understand, due to the lack of good documentation.  This
  30. seminar will attempt to address this subject.
  31.  
  32. Disclaimer:  I do not claim to know everything about this subject.  I
  33. simply know enough about this subject to warrant the writing of this
  34. seminar with the intent of making it easier on those who want to know
  35. more.
  36.  
  37. Audience:  this seminar is directed at C programmers with good PM
  38. programming experience.  Function prototypes will be taken from the OS/2
  39. 1.3 toolkit.
  40.  
  41. Contact:  any questions, comments, etc. can be sent directly to me at one
  42. of the addresses listed above (in the order of preference and highest
  43. rate of successful delivery).
  44.  
  45.                            Device Independence
  46.                            -------------------
  47.  
  48. OS/2 dubiously touts a feature called device independence, which many
  49. have heard of but few know much about.  In a sentence, device
  50. independence means that what your application does on one output device
  51. can be done on any other output device, provided that the new device has
  52. the physical capabilities required to perform the actions requested (I
  53. don't want to get any notes asking why you can't get color output from a
  54. black-and-white printer!!!)  and provided that you do the proper steps to
  55. setup any output device that isn't the display.  To put this in practical
  56. terms, GpiCharStringAt on a display will work the same on a printer,
  57. plotter, etc.  (also known as the "a rose is a rose is a rose etc."
  58. syndrome :).
  59.  
  60. Of course, you'll notice the phrase "proper steps to setup...".  What
  61. does this mean?  On a printer, it means that you told the spooler (here,
  62. I am assuming that printing will go through the spooler) what printer to
  63. use and any special job properties that are to be associated with this
  64. output job.
  65.  
  66.                              Device Contexts
  67.                              ---------------
  68.  
  69. The method used to setup a printer for output is by using device
  70. contexts.  A device context is simply a data structure describing, among
  71. other things, the characteristics and capabilities of the device in
  72. question.  Device contexts can be manipulated using the Dev API group.
  73.  
  74. The first step, however, is to acquire a device context handle.  This is
  75. done using the DevOpenDC call:
  76.  
  77.    HDC APIENTRY DevOpenDC(HAB hab,
  78.                           LONG lType,
  79.                           PSZ pszToken,
  80.                           LONG lCount,
  81.                           PDEVOPENDATA pdopData,
  82.                           HDC hdcComp);
  83.  
  84. hab - the anchor block handle of the calling thread
  85. lType - one of the OD_* constants.  We will be using OD_QUEUED and
  86.    OD_MEMORY
  87. pszToken - a token into the OS2.INI file used for obtaining the default
  88.    job specific data.  "*" means that no data is to be taken from OS2.INI
  89. lCount - the number of items initialized in the structure pointed
  90.    to by pdopData.
  91. pdopData - a pointer to a DEVOPENSTRUC who's first 'lCount' items are
  92.    properly initialized.
  93. hdcComp - for OD_MEMORY device contexts, the device context handle of a
  94.    compatible device.  If NULL, OS/2 assumes compatibility with the
  95.    display.
  96.  
  97. DevOpenDC returns a device context handle if successful, DEV_ERROR
  98. otherwise.  We will use, for the sake of example, the following call:
  99.  
  100.         hdcPrinter=DevOpenDC(hab,OD_QUEUED,"*",9L,&dosPrinter,NULL);
  101.  
  102. For now, we will only use OD_QUEUED HDC's, meaning that hdcComp will
  103. always be NULL.  Later, when we discuss bitmaps, we will use OD_MEMORY
  104. contexts.  We will see later how to initialize the DEVOPENSTRUC
  105. (dosPrinter) structure properly.
  106.  
  107. So now we have a device context handle.  Big deal, right?  The fun
  108. actually begins after we create a presentation space using this device
  109. context handle as the second parameter:
  110.  
  111.         szlPage.cx=0;
  112.         szlPage.cy=0;
  113.  
  114.         hpsPrinter=GpiCreatePS(hab,
  115.                                hdcPrinter,
  116.                                &szlPage,
  117.                                PU_TWIPS|GPIT_MICRO|GPIA_ASSOC);
  118.  
  119. Assuming, of course, that both DevOpenDC and GpiCreatePS succeed, we are
  120. ready to start drawing in hpsPrinter.  Any of the Gpi calls can be used
  121. (if GPIT_MICRO is used on GpiCreatePS, then the Gpi calls are restricted
  122. to those that work with micro-PS's.) and we will see the corresponding
  123. output from the printer...
  124.  
  125. ...almost.  The painted picture isn't quite complete.  In fact, I left a
  126. lot out for simplicity's sake.  The first of the two non-existant steps
  127. is...
  128.  
  129.                        The DEVOPENSTRUC Structure
  130.                        --------------------------
  131.  
  132. Okay, before I said I would explain how to initialize the DEVOPENSTRUC
  133. structure passed to the DevOpenDC call:
  134.  
  135.         typedef struct _DEVOPENSTRUC { /* dop */
  136.             PSZ pszLogAddress;
  137.             PSZ pszDriverName;
  138.             PDRIVDATA pdriv;
  139.             PSZ pszDataType;
  140.             PSZ pszComment;
  141.             PSZ pszQueueProcName;
  142.             PSZ pszQueueProcParams;
  143.             PSZ pszSpoolerParams;
  144.             PSZ pszNetworkParams;
  145.         } DEVOPENSTRUC;
  146.         typedef DEVOPENSTRUC FAR *PDEVOPENSTRUC;
  147.  
  148. pszLogAddress - points to the queue name.
  149. pszDriverName - points to the printer driver name.
  150. pdriv - points to the printer driver-specific data to be used for this
  151.    job (see below).
  152. pszDataType - points to the device driver data type (Details whether or
  153.    not to use "raw" data or "standard" data, the former being ASCII
  154.    characters and printer-control codes.  This is not desirable when
  155.    programming for a device independent environment, so I always use
  156.    "PM_Q_STD")
  157. pszComment - points to a comment string to be included as part of the job
  158.    details
  159. pszQueueProcName - points to the name of the queue processor to be used.
  160.    If NULL, the system default queue processor is used (I always put NULL
  161.    here).
  162. pszQueueProcParams - points to the queue processor parameter string if
  163.    OD_QUEUED is used (see below).
  164. pszSpoolerParams - points to the spooler parameter string if OD_QUEUED is
  165.    used (see below).
  166. pszNetworkParams - points to a network parameter string if OD_QUEUED is
  167.    used (I always put NULL here).
  168.  
  169. Starting with the easiest "see below"'s...
  170.  
  171. pszSpoolerParams is a string of parameters separated by blanks to be
  172. passed to the spooler.  Currently, only the following parameter is
  173. defined:
  174.         PRTY=n:         - Sets the job priority to 'n'.  It must be in
  175.                           the range 0 (lowest) to 99 (highest).
  176.  
  177. pszQueueProcParams is a string of parameters separated by blanks to be
  178. passed to the queue print processor.  For the PMPRINT processor (PMPLOT
  179. also exists, but I have no experience with it), the following parameters
  180. are defined (taken from the Programming Guide, chapter 37):
  181.         COP=n           - The number of copies to print.  The default is
  182.                           COP=1.
  183.         ARE=C | w,h,l,t - The size and position of the output area.
  184.                           ARE=C means that the output area is the whole
  185.                           page, while w,h,l,t can be specified to
  186.                           restrict this.  'w' is the width, 'h' is the
  187.                           height, 'l' is the offset from the left of the
  188.                           maximum area to the left of the output area,
  189.                           and 't' is the offset from the top of the
  190.                           maximum area to the top of the output area.
  191.                           The default is ARE=C.
  192.         FIT=S | l,t     - The part of the picture that is to be printed
  193.                           in the output area.  FIT=S causes the output to
  194.                           be scaled until either the width or height
  195.                           reaches the boundary of the output area,
  196.                           maintaining the aspect ratio of the output,
  197.                           while l,t can be specified to print in "real
  198.                           size".  'l' is the percentage of the output in
  199.                           the horizontal direction (from the left edge)
  200.                           that is to be centered (horizontally) in the
  201.                           output area. 't' is the percentage of the
  202.                           output in the vertical direction (from the top
  203.                           edge) that is to be centered (vertically) in
  204.                           the output area.  Thus, "FIT=50,50" means that
  205.                           the picture is to be centered in the output
  206.                           area (50 percent from the left and 50 percent
  207.                           from the top is the center), while "FIT=0,100"
  208.                           means that the lower left corner is the center
  209.                           of the output area.  The default is "FIT=S".
  210.         XFM=0 | 1       - Specifies whether to override the picture
  211.                           positioning and clipping instructions given by
  212.                           the ARE and FIT parameters.  0 means use the
  213.                           output as specified in the picture file while 1
  214.                           means use the ARE and FIT settings.  The
  215.                           default is "XFM=1" (Author's note:  don't
  216.                           worry, I don't understand what the heck they
  217.                           are saying here either!).
  218.         COL=M | C       - Specifies whether or not to use color.  M means
  219.                           create monochrome output and is supported by
  220.                           all devices.  C means create color output.  If
  221.                           the device supports color output, this can be
  222.                           nice, but if the device does not support color
  223.                           output, this causes problems since the only
  224.                           color is black (meaning red lines on a blue
  225.                           background will become black line on black
  226.                           background!).  The default is "COL=M" if using
  227.                           a monochrome device, "COL=C" if using a color
  228.                           device.
  229.         MAP=N | A       - Specifies how to map neutral colors.  N means
  230.                           create a "normal" reprentation (black on
  231.                           white), while A means create an "inverted"
  232.                           representation (white on black).  The default
  233.                           is "MAP=N".
  234.  
  235. Finally, pdriv points to the printer driver specific data to be used for
  236. this job.  This data is obtained using the DevPostDeviceModes call:
  237.  
  238. LONG APIENTRY DevPostDeviceModes(HAB hab,
  239.                                  PDRIVDATA pdrivDriverData,
  240.                                  PSZ pszDriverName,
  241.                                  PSZ pszDeviceName,
  242.                                  PSZ pszName,
  243.                                  ULONG flOptions);
  244.  
  245. hab - the anchor block handle of the calling thread
  246. pdrivDriverData - pointer to a buffer to receive the printer driver
  247.    specific data (see below).
  248. pszDriverName - points to the name of the printer driver
  249. pszDeviceName - points to the name of the device.  For printer drivers
  250.    that support only one printer, this is not required.
  251. pszName - points to the printer name.
  252. flOptions - one of the DPDM_* constants.
  253.  
  254. The action taken depends on both pdrivDriverData and flOptions.  If
  255. pdrivDriverData is NULL, then the size of the buffer needed to hold the
  256. driver data is returned.  Otherwise, the value of flOptions dictates the
  257. action taken:
  258.  
  259.         DPDM_QUERYJOBPROP       - initializes pdrivDriverData with the
  260.                                   current job properties.
  261.         DPDM_POSTJOBPROP        - displays a dialog box allowing the user
  262.                                   to change the job properties.  If
  263.                                   pszName is not NULL, pdrivDriverData is
  264.                                   initialized first with the data from
  265.                                   OS2.INI. Otherwise, the values
  266.                                   contained in pdrivDriverData are used.
  267.         DPDM_CHANGEJOBPROP      - displays two dialog boxes, the first
  268.                                   allowing the user to change the job
  269.                                   properties and the second allowing the
  270.                                   user to change the printer properties.
  271.                                   pszName cannot be NULL.
  272.  
  273. Doing this is ***most*** important, especially since printer jobs going
  274. to LAN printers will not work unless pdrivData points to valid data.
  275. We'll use the following code to initialize the data and give the user the
  276. opportunity to change the properties:
  277.  
  278.         ULONG ulSzBuf;
  279.         PDRIVDATA pddData;
  280.  
  281.         ulSzBuf=DevPostDeviceModes(hab,
  282.                                    NULL,
  283.                                    "PSCRIPT",
  284.                                    "IBM 4019 v_52 (39 Fonts)",
  285.                                    "LANPRN",
  286.                                    DPDM_QUERYJOBPROP);
  287.  
  288.         DosAllocSeg((SHORT)ulSzBuf,&SELECTOROF(pddData),0);
  289.         OFFSETOF(pddData)=0;
  290.  
  291.         DevPostDeviceModes(hab,
  292.                            pddData,
  293.                            "PSCRIPT",
  294.                            "IBM 4019 v_52 (39 Fonts)",
  295.                            "LANPRN",
  296.                            DPDM_QUERYJOBPROP);
  297.         DevPostDeviceModes(hab,
  298.                            pddData,
  299.                            "PSCRIPT",
  300.                            "IBM 4019 v_52 (39 Fonts)",
  301.                            "LANPRN",
  302.                            DPDM_POSTJOBPROP);
  303.  
  304.                      Device Independence (Revisited)
  305.                      -------------------------------
  306.  
  307. WHOA!!!  If OS/2 is so device independent, where do the hard-coded values
  308. come from?  Those hard coded values should ideally come from OS2.INI,
  309. since the user can define, change, and delete printer definitions at
  310. will.  Given that we can obtain all of the printer names defined to the
  311. system using the PrfQueryProfileString call with
  312. pszAppName=SPL_INI_PRINTER and pszKeyName=NULL (which means enumerate all
  313. keys defined for the application specified), we can obtain the rest of
  314. the information using the following code:
  315.  
  316.         typedef struct {
  317.            CHAR achName[64];
  318.            CHAR achQueue[64];
  319.            CHAR achDriver[64];
  320.            CHAR achDevice[64];
  321.            CHAR achPort[8];
  322.         } PRINTERINFO, FAR *PPRINTERINFO;
  323.  
  324.         BOOL QueryPrinterInfo(PPRINTERINFO ppiInfo)
  325.         //-------------------------------------------------------
  326.         // This function queries OS2.INI for information about the
  327.         // printer who's name is given in the achName field of the
  328.         // PRINTERINFO structure pointed to by ppiInfo.
  329.         //
  330.         // Input:  ppiInfo - points to a PRINTERINFO structure with
  331.         //                   achName initialized to the printer
  332.         //                   name.
  333.         // Output:  ppiInfo - points to a completely initialized
  334.         //                    PRINTERINFO structure
  335.         // Returns:  TRUE if successful, FALSE otherwise
  336.         //
  337.         // Written by:  Larry Salomon, Jr.
  338.         //-------------------------------------------------------
  339.         {
  340.            ULONG ulSzBuf;
  341.            PCHAR pchData;
  342.            PCHAR pchPos;
  343.  
  344.            if ((!PrfQueryProfileSize(HINI_PROFILE,
  345.                                      SPL_INI_PRINTER,
  346.                                      ppiInfo->achName,
  347.                                      &ulSzBuf)) || (ulSzBuf==0)) {
  348.               return FALSE;
  349.            } /* endif */
  350.  
  351.            pchData=(PCHAR)malloc((SHORT)ulSzBuf);
  352.            if (pchData==NULL) {
  353.               return FALSE;
  354.            } /* endif */
  355.  
  356.            ppiInfo->achQueue[0]=0;
  357.            ppiInfo->achDriver[0]=0;
  358.            ppiInfo->achDevice[0]=0;
  359.            ppiInfo->achPort[0]=0;
  360.  
  361.            PrfQueryProfileString(HINI_PROFILE,
  362.                                  SPL_INI_PRINTER,
  363.                                  ppiInfo->achName,
  364.                                  "",
  365.                                  pchData,
  366.                                  ulSzBuf);
  367.  
  368.            pchPos=strchr(pchData,';')+1;
  369.            pchPos=strchr(pchPos,';')+1;
  370.            *(pchPos+strcspn(pchPos,".,;"))=0;
  371.            strcpy(ppiInfo->achQueue,pchPos);
  372.  
  373.            pchPos=strchr(pchData,';')+1;
  374.            pchPos+=strcspn(pchPos,".;");
  375.            if (*pchPos=='.') {
  376.               pchPos++;
  377.               *(pchPos+strcspn(pchPos,",;"))=0;
  378.               strcpy(ppiInfo->achDevice,pchPos);
  379.            } else {
  380.               ppiInfo->achDevice[0]=0;
  381.            } /* endif */
  382.  
  383.            pchPos=strchr(pchData,';')+1;
  384.            *(pchPos+strcspn(pchPos,".,;"))=0;
  385.            strcpy(ppiInfo->achDriver,pchPos);
  386.  
  387.            *strchr(pchData,';')=0;
  388.            strcpy(ppiInfo->achPort,pchData);
  389.  
  390.            free(pchData);
  391.            return TRUE;
  392.         }
  393.  
  394. The achName, achDriver and achDevice fields can then be used in the
  395. DevPostDeviceModes call(s) to obtain the printer driver data to be used
  396. in the DEVOPENSTRUC structure for the DevOpenDC call.
  397.  
  398. It really is easy, though.  Honest.
  399.  
  400.                       The Second Non-Existant Step
  401.                       ----------------------------
  402.                                    or
  403.                        How to Start the Print Job
  404.                        --------------------------
  405.  
  406. The final step that I left out way back yonder is actually telling the
  407. spooler that it should ready itself to create the job output.  This is
  408. comparitively easy (compared to the DevPostDeviceModes call and
  409. associated stuff) using the DevEscape call.
  410.  
  411. LONG APIENTRY DevEscape(HDC hdc,
  412.                         LONG lCode,
  413.                         LONG lInCount,
  414.                         PBYTE pbInData,
  415.                         PLONG plOutCount,
  416.                         PBYTE pbOutData);
  417.  
  418. hdc - the device context handle to send the escape code to.
  419. lCode - one of the DEVESC_* constants
  420. lInCount - the size of the data pointed to by pbInData
  421. pbInData - input data which varies according to the DEVESC code
  422. plOutCount - points to the size of the data pointed to by pbOutData.
  423.    This is updated with the actual size of the data returned.
  424. pbOutData - output data which varies according to the DEVESC code
  425.  
  426. While there are many DEVESC_* codes, we are interested in the following
  427. ones only:
  428.  
  429.         DEVESC_STARTDOC - Starts the print job.  pbInData points to the
  430.                           name of the print job and lInCount contains the
  431.                           and lInCount contains the length of the print
  432.                           job name.
  433.         DEVESC_ENDDOC   - Ends the print job.  pbOutData points to a
  434.                           16-bit integer which will contain the job id.
  435.         DEVESC_ABORTDOC - Aborts the print job.  None of the other
  436.                           parameters are used.
  437.         DEVESC_NEWFRAME - Starts a new page.  None of the other
  438.                           parameters are used.
  439.  
  440.                                Miscellany
  441.                                ----------
  442.  
  443. The final item that I wish to mention is the DevQueryCaps call.  This API
  444. allows you to actually query the device capabilities.
  445.  
  446. BOOL APIENTRY DevQueryCaps(HDC hdc,
  447.                            LONG lStart,
  448.                            LONG lCount,
  449.                            PLONG alArray);
  450.  
  451. hdc - the device context handle to query.
  452. lStart - the index of the starting capability to query (one of the CAPS_*
  453.    constants).
  454. lCount - the number of capabilities to query.
  455. alArray - points to an array of lCount LONGs, to receive the query
  456.    results.
  457.  
  458. There are many CAPS_* constants, the ones I am usually interested in
  459. being:
  460.  
  461.         CAPS_HEIGHT             - returns the maximum output height in
  462.                                   pels
  463.         CAPS_WIDTH              - returns the maximum output width in
  464.                                   pels
  465.         CAPS_COLORS             - returns the maximum number of colors
  466.                                   the device supports
  467.         CAPS_COLOR_PLANES       - returns the number of color planes
  468.         CAPS_COLOR_BITCOUNT     - returns the number of bits per pel used
  469.                                   for color
  470.  
  471. The information returned for the first three is intuitively obvious, and
  472. the last two we'll see again in part 2.
  473.  
  474.                           Wrapping Up
  475.                           --------------------
  476.  
  477. After this is all done, we can use the Gpi calls to draw lines, points,
  478. text, and even bit-blit to the presentation space associated with the
  479. printer and see the results with our very own eyes.  The order of calls
  480. is:
  481.  
  482.         DevPostDeviceModes(NULL)
  483.         DevPostDeviceModes(pddData)
  484.         DevOpenDC(...)
  485.         GpiCreatePS(...)
  486.         DevEscape(DEVESC_STARTDOC);
  487.                 :
  488.                 : /* Do your drawing here */
  489.                 :
  490.         if (bError) {
  491.            DevEscape(DEVESC_ABORTDOC);
  492.         } /* endif */
  493.  
  494.         DevEscape(DEVESC_ENDDOC);
  495.         GpiDestroyPS(...)
  496.         DevCloseDC(...)
  497.  
  498. Note that between the start/stop document escapes, you can create/set
  499. logical fonts, change colors (although you might not see any change),
  500. etc. and it works!!!
  501.