Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

A text print class

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Charles Dietz
    replied
    This is a slight revision or the TPrint class with a few improvements and more comments. It can be considered as in the Public Domain, free to use in any manner whatsoever.

    Code:
    '===================================================================================
    'Text Print Class - requires PBWin90
    'by Charles Dietz (03-27-09)
    '[email protected]
    '-----------------------------------------------------------------------------------
    'Using the Text Print class:
    '
    '   LOCAL oTPrint AS iTPrint
    '   LET oTPrint = CLASS "cTPrint"
    '
    '   printerName = oTPrint.choosePrinter
    '   printerName = oTPrint.defaultPrinter
    '   hdc = oTPrint.hdc
    '   oTPrint.orientPrint(2)
    '   oTPrint.duplexPrint(2)
    '   numLines = oTPrint.getLines
    '   oTPrint.setMargins(.75, .50, .75, 1.00)
    '   oTPrint.beginDoc("Job name")
    '   oTPrint.beginPage
    '   oTPrint.getPageSize(wi, ht)
    '   oTPrint.newFont("MS Sans Serif", 10, "bui")
    '   oTPrint.getCharSize(wi, ht)
    '   wi = oTPrint.getTextWidth(sText)
    '   sText = oTPrint.truncate(sText, 2.50)
    '   oTPrint.setPos(2.00, 1.00)
    '   oTPrint.printText(sText, 2.00, 1.00)
    '   oTPrint.printText(sText)
    '   oTPrint.closePage
    '   oTPrint.closeDoc
    '
    'Note:
    '   -  Define a TPrint object with:
    '         LOCAL oTPrint AS iTPrint
    '         LET oTPrint = CLASS "cTPrint"
    '   -  The first command after the TPrint object is defined must either 
    '      be choosePrinter() or defaultPrinter()
    '   -  Printing will not occur until closePage and closeDoc are executed
    '   -  closeDoc() cleans up in order to prevent resource leaks 
    '   -  beginDoc() should be placed after defining orient, duplex, font, ...
    '
    '===================================================================================
    
    '... these two include statements not needed if InClean optimizer is used
    #INCLUDE ONCE "WIN32API.INC"         
    #INCLUDE ONCE "ComDlg32.Inc"
    
    CLASS cTPrint
       INSTANCE hdc AS LONG               'handle of a display device context 
       INSTANCE hFont AS LONG             'handle of current font
       INSTANCE hOrigFont AS LONG         'handle of original font
       INSTANCE charHt AS DOUBLE          'character height
       INSTANCE leftMargin AS DOUBLE      'left margin
       INSTANCE rightMargin AS DOUBLE     'right margin
       INSTANCE topMargin AS DOUBLE       'top margin
       INSTANCE bottomMargin AS DOUBLE    'bottom margin
       INSTANCE printerName AS STRING     'name of printer
       INSTANCE hDevMode AS LONG          'handle to DEVMODE structure
       INSTANCE origDuplex AS LONG        'original duplex mode
       INSTANCE origOrient AS LONG        'original orientation mode
       INSTANCE xPos, yPos AS DOUBLE      'default position for text print
    
       CLASS METHOD reportError(OPT BYVAL n AS LONG)
          LOCAL s AS STRING
          IF n = 1 THEN
             s = "Document printing error"
          ELSEIF n = 2 THEN
             s = "Page printing error"
          ELSE                        
             s = "Unspecified printer error"
          END IF
          MSGBOX s, %MB_ICONERROR, "Printer Error"
       END METHOD
       
       INTERFACE iTPrint
          INHERIT IUNKNOWN
          
          PROPERTY GET hdc() AS LONG 
             PROPERTY = hdc
          END PROPERTY
          
          METHOD defaultPrinter() AS STRING
             'determine default printer and device context handle
             LOCAL hPrinter, dwNeeded, n AS LONG
             LOCAL tm AS TEXTMETRIC, sz AS ASCIIZ*128
             LOCAL pDevMode AS DEVMODE PTR
             GetProfileString "WINDOWS", "DEVICE", "", sz, 127
             sz = TRIM$(PARSE$(sz, ",", 1)): printerName = sz
             OpenPrinter(sz, hPrinter, BYVAL %NULL) 'to obtain hPrinter
             dwNeeded = DocumentProperties(0, hPrinter, sz, BYVAL %NULL, BYVAL %NULL, 0)
             hDevMode = GlobalAlloc(%GHND, dwNeeded)
             pDevMode = GlobalLock(hDevMode)
             DocumentProperties 0, hPrinter, sz, BYVAL pDevMode, BYVAL %NULL, %DM_OUT_BUFFER 
             hdc = CreateDC("WINSPOOL", sz, BYVAL %NULL, BYVAL pDevMode)
             GetTextMetrics hdc, tm 
             n = tm.tmHeight + tm.tmExternalLeading 
             charHt = n / GetDeviceCaps(hDC, %LOGPIXELSY)
             METHOD = printerName
          END METHOD
    
          METHOD choosePrinter() AS STRING
             'choose printer and determine device context handle 
             LOCAL n AS LONG 
             LOCAL pd AS PRINTDLGAPI, pDevNames AS DEVNAMES PTR
             LOCAL tm AS TEXTMETRIC, psz AS ASCIIZ PTR
             pd.lStructSize = SIZEOF(pd)
             pd.Flags = %PD_RETURNDC OR %PD_HIDEPRINTTOFILE
             pd.Flags = pd.Flags OR %PD_ALLPAGES OR %PD_NOSELECTION OR %PD_NOPAGENUMS
             IF PrintDlg(pd) THEN 'call print dialog to select printer 
                pDevNames = GlobalLock(pd.hDevNames)
                psz = pDevNames + @pDevNames.wDeviceOffset: printerName = @psz
                GlobalUnlock pd.hDevnames: hdc = pd.hDC
             END IF 
             METHOD = printerName
             GetTextMetrics hdc, tm 
             n = tm.tmHeight + tm.tmExternalLeading
             charHt = n / GetDeviceCaps(hDC, %LOGPIXELSY)
             hDevMode = pd.hDevMode
          END METHOD
          
          METHOD newFont(fName AS STRING, fSize AS LONG, OPT BYVAL fAttr AS STRING) AS LONG
             'define a new font using a font name, font size in points, and font attributes
             'combine attributes: "b" for bold, "u" for underline, "i" for italic 
             LOCAL yppi, charSize, n AS LONG
             LOCAL wt, nBold, underline, italic AS DWORD, s AS STRING
             LOCAL tm AS TEXTMETRIC
             IF hFont THEN DeleteObject hFont
             IF hdc = 0 THEN ME.defaultPrinter
             IF LEN(fAttr) THEN
                nBold = SGN(INSTR(fAttr, "b"))*300
                underline = INSTR(fAttr,"u")
                italic = INSTR(fAttr,"i")
             END IF
             yppi  = GetDeviceCaps(hDC, %LOGPIXELSY)
             charSize = (fSize * yppi) \ 72
             wt = %FW_NORMAL + nBold
             hFont = CreateFont(-charSize, 0, 0, 0, wt, italic, underline, 0, _
                     %ANSI_CHARSET, %OUT_TT_PRECIS, %CLIP_DEFAULT_PRECIS, _
                     %DEFAULT_QUALITY, %FF_DONTCARE, BYCOPY fName)
             n = SelectObject(hdc, hFont)
             IF hOrigFont = 0 THEN hOrigFont = n
             GetTextMetrics hdc, tm 
             n = tm.tmHeight + tm.tmExternalLeading
             charHt = n / GetDeviceCaps(hDC, %LOGPIXELSY)
          END METHOD 
    
          METHOD getTextWidth(txt AS STRING) AS DOUBLE
             'get the text width in inches 
             LOCAL cSize AS SIZEL
             IF hdc = 0 THEN ME.defaultPrinter
             GetTextExtentPoint32 hdc, BYVAL STRPTR(txt), LEN(txt), cSize
             METHOD = cSize.cx/GetDeviceCaps(hdc, %LOGPIXELSX)
          END METHOD
          
          METHOD truncate(s AS STRING, BYVAL wi AS DOUBLE) AS STRING
             'truncate the text to a specified width, in inches
             'if wi > 0, truncate the back end, else truncate the front end
             LOCAL nFit AS LONG, cSize AS SIZEL, ss AS STRING
             IF hdc = 0 THEN ME.defaultPrinter
             wi = GetDeviceCaps(hdc, %LOGPIXELSX) * wi 'width in pixels (logical units)
             IF wi > 0 THEN
                ss = s
                GetTextExtentExPoint hdc, BYVAL STRPTR(ss), LEN(ss), wi, nFit, BYVAL %NULL, cSize
                METHOD = LEFT$(ss, nFit)
             ELSEIF wi < 0 THEN
                wi = ABS(wi): ss = STRREVERSE$(s)
                GetTextExtentExPoint hdc, BYVAL STRPTR(ss), LEN(ss), wi, nFit, BYVAL %NULL, cSize
                METHOD = STRREVERSE$(LEFT$(ss, nFit))
             END IF
          END METHOD
       
          METHOD setMargins(nLeft AS DOUBLE, nRight AS DOUBLE, nTop AS DOUBLE, nBottom AS DOUBLE)
             'set page margins
             leftMargin = nLeft: rightMargin = nRight: topMargin = nTop: bottomMargin = nBottom
          END METHOD
          
          METHOD getPageSize(wi AS DOUBLE, ht AS DOUBLE)
             'total size of the host printer page in inches
             IF hdc = 0 THEN ME.defaultPrinter
             wi = GetDeviceCaps(hdc, %PHYSICALWIDTH)/GetDeviceCaps(hdc, %LOGPIXELSX)
             ht = GetDeviceCaps(hdc, %PHYSICALHEIGHT)/GetDeviceCaps(hdc, %LOGPIXELSY)
          END METHOD
          
          METHOD getCharSize(wi AS DOUBLE, ht AS DOUBLE)
             'character width and height in inches
             'average character width if proportional font
             LOCAL tm AS TEXTMETRIC
             IF hdc = 0 THEN ME.defaultPrinter
             GetTextMetrics hdc, tm
             wi = tm.tmAveCharWidth/GetDeviceCaps(hdc, %LOGPIXELSX)
             ht = charHt
          END METHOD
          
          METHOD getLines(OPT BYVAL y AS DOUBLE) AS LONG
             'determine number of remaining lines from y to bottom margin
             LOCAL yppi, paperHt, topNoPrn, bottomNoPrn AS LONG
             LOCAL yn AS DOUBLE
             IF hdc = 0 THEN ME.defaultPrinter
             yppi = GetDeviceCaps(hdc, %LOGPIXELSY)
             paperHt = GetDeviceCaps(hdc, %PHYSICALHEIGHT)
             topNoPrn = GetDeviceCaps(hdc, %PHYSICALOFFSETY)
             bottomNoPrn = paperHt - topNoPrn - GetDeviceCaps(hdc, %VERTRES)
             y = MAX(topMargin, topNoPrn\yppi, y)
             yn = paperHt/yppi - MAX(bottomMargin, bottomNoPrn/yppi) - y 
             METHOD = INT(yn / charHt)
          END METHOD
          
          METHOD duplexPrint(n AS LONG)
             'n = 1 Simplex
             'n = 2 Horizontal
             'n = 3 Vertical
             '-------------------------------------------------
             LOCAL pDevMode AS DEVMODE PTR 
             IF hDevMode = 0 OR n = 0 THEN EXIT METHOD
             pDevMode = GlobalLock(hDevMode)
             IF origDuplex = 0 THEN origDuplex = @pDevMode.dmDuplex
             IF @pDevMode.dmFields AND %DM_DUPLEX THEN 
                @pDevMode.dmDuplex = n
                @pDevMode.dmFields = @pDevMode.dmFields OR %DM_DUPLEX  
                ResetDC hDC, @pDevMode
                GlobalUnlock hDevMode
             END IF
          END METHOD 
          
          METHOD orientPrint(n AS LONG)
             'n = 1 Portrait
             'n = 2 Landscape
             '-------------------------------------------------
             LOCAL pDevMode AS DEVMODE PTR 
             IF hDevMode = 0 THEN EXIT METHOD 
             pDevMode = GlobalLock(hDevMode)
             IF origOrient = 0 THEN origOrient = @pDevMode.dmOrientation
             IF n AND @pDevMode.dmFields AND %DM_ORIENTATION THEN
                IF n THEN @pDevMode.dmOrientation = n 
                @pDevMode.dmFields = @pDevMode.dmFields OR %DM_ORIENTATION  
                ResetDC hDC, @pDevMode
                GlobalUnlock hDevMode
             END IF 
          END METHOD 
          
          METHOD setPos(x AS DOUBLE, y AS DOUBLE)
             xPos = x: yPos = y
          END METHOD
          
          METHOD printText(OPT BYVAL txt AS STRING, BYVAL x AS DOUBLE, BYVAL y AS DOUBLE)
             'x, y are measured from the left and top edges of the paper
             'if x or y are omitted then last valuew, xLast, yLast are used
             LOCAL xc, yc, xm, leftNoPrn, rightNoPrn, topNoPrn, bottomNoPrn AS LONG 
             LOCAL paperWi, paperHt, xMax, yMax, xppi, yppi AS LONG
             LOCAL s AS STRING, sWidth AS DOUBLE, cSize AS SIZEL
             s = txt: IF x THEN xPos = x ELSE x = xPos
             IF y THEN yPos = y ELSE y = yPos + charHt: yPos = y
             IF hdc = 0 THEN ME.defaultPrinter
             paperWi = GetDeviceCaps(hdc, %PHYSICALWIDTH)
             paperHt = GetDeviceCaps(hdc, %PHYSICALHEIGHT)
             leftNoPrn = GetDeviceCaps(hdc, %PHYSICALOFFSETX)
             rightNoPrn = paperWi - leftNoPrn - GetDeviceCaps(hdc, %HORZRES)
             topNoPrn = GetDeviceCaps(hdc, %PHYSICALOFFSETY)
             bottomNoPrn = paperHt - topNoPrn - GetDeviceCaps(hdc, %VERTRES)
             GetTextExtentPoint32 hdc, BYVAL STRPTR(s), LEN(s), cSize
             xppi = GetDeviceCaps(hdc, %LOGPIXELSX): yppi = GetDeviceCaps(hdc, %LOGPIXELSY)
             xMax = paperWi - leftNoPrn - xppi * rightMargin
             yMax = paperHt - topNoPrn - yppi * bottomMargin
             xc = MAX(xppi * x - leftNoPrn, 0)
             yc = MAX(yppi * y - topNoPrn, 0)
             xm = MAX(xppi * leftMargin - leftNoPrn, 0)
             IF x < leftMargin THEN 'text overlaps left margin
                sWidth = ME.getTextWidth(s)
                s = ME.truncate(s, -(sWidth + x - leftMargin)): xc = xm 
             END IF 
             IF xc + cSize.cx > xMax THEN 'text overlaps right margin
                s = ME.truncate(s, (xMax - xc)/xppi)
             END IF
             IF y < topMargin THEN EXIT METHOD   'text overlaps top margin
             IF yc + yppi * charHt > yMax THEN EXIT METHOD  'text overlaps bottom margin
             TextOut(hdc, xc, yc, BYVAL STRPTR(s), LEN(s))
          END METHOD
    
          METHOD beginDoc(s AS STRING)
             LOCAL nErr AS LONG, sz AS ASCIIZ*64
             LOCAL di AS DOCINFO
             IF hdc = 0 THEN ME.defaultPrinter
             sz = s 
             di.cbSize = SIZEOF(di)
             di.lpszDocName = VARPTR(sz)
             nErr = StartDoc(hdc, di)
             IF nErr <= 0 THEN ME.reportError(1)
          END METHOD
          
          METHOD beginPage
             LOCAL nErr AS LONG
             nErr = StartPage(hdc)
             IF nErr <= 0 THEN ME.reportError(2)
          END METHOD
          
          METHOD closePage
             EndPage(hdc)
          END METHOD
          
          METHOD closeDoc 
             EndDoc(hdc)
             SelectObject hdc, hOrigFont
             DeleteObject hFont
             deleteDC hdc
             origDuplex = 0
             origOrient = 0
             hOrigFont = 0
          END METHOD
          
       END INTERFACE
    END CLASS

    Leave a comment:


  • Charles Dietz
    replied
    A simple example for testing the TPrint class follows:

    Code:
    #COMPILE EXE
    #DIM ALL
    #INCLUDE "Win32Api.Inc"
    #INCLUDE "ComDlg32.Inc"
    #INCLUDE "TPrint.inc"
    
    FUNCTION PBMAIN()
       LOCAL i AS LONG, s AS STRING
       LOCAL oTPrint AS iTPrint
       LET oTPrint = CLASS "cTPrint"
       s = oTPrint.choosePrinter
       's = oTPrint.defaultPrinter 
       IF LEN(s) = 0 THEN EXIT FUNCTION
       oTPrint.orientPrint 2
       oTPrint.duplexPrint 2
       oTPrint.beginDoc "Text Print"
       oTPrint.beginPage
       s = "This is side 1"
       oTPrint.setPos(2, 1)
       FOR i = 1 TO 10
          oTPrint.printText s
       NEXT i
       oTPrint.closePage
       oTPrint.beginPage
       s = "This is side 2"
       oTPrint.printText s, 2, 1
       FOR i = 1 TO 9
          oTPrint.printText s
       NEXT i
       oTPrint.closePage
       oTPrint.closeDoc 
    END FUNCTION

    Leave a comment:


  • Charles Dietz
    started a topic A text print class

    A text print class

    Although the PB XPRINT routines can be used to print text, they are actually directed toward graphics based applications. For strictly text page printing, I think that a better choice is to print them with routines based on the following Windows API. In the one application that I compared the two approaches, the executable file using XPRINT routines required 4K more memory. Also, my Brother HL-5250DN printer would not respond to the XPRINT DUPLEX command, whereas it does print both sides with the following duplex routine.

    The code below is compiled with PBWin 9.01 as an include file, "TPrint.inc".


    Code:
    '===================================================================================
    'Text Print Class
    'by Charles Dietz (03-27-09)
    '[email protected]
    '-----------------------------------------------------------------------------------
    'Using the Text Print class:
    '
    '   LOCAL oTPrint AS iTPrint
    '   LET oTPrint = CLASS "cTPrint"
    '
    '   printerName = oTPrint.choosePrinter
    '   printerName = oTPrint.defaultPrinter
    '   hdc = oTPrint.hdc
    '   orig = oTPrint.orientPrint(2)
    '   orig = oTPrint.duplexPrint(2)
    '   numLines = oTPrint.getLines
    '   oTPrint.setMargins .75, .50, .75, 1.00
    '   oTPrint.beginDoc "Job name"
    '   oTPrint.beginPage
    '   oTPrint.newFont "MS Sans Serif", 10, "bui"
    '   textWi = oTPrint.getWidth(s)
    '   sText = oTPrint.truncate(sText, 2.50)
    '   oTPrint.setPos(2.00, 1.00)
    '   oTPrint.printText(sText, 2.00, 1.00)
    '   oTPrint.printText sText
    '   cht = oTPrint.charHt
    '   oTPrint.closePage
    '   oTPrint.closeDoc
    '
    'Note:  
    '   -  The first command after the TPrint object is defined must either 
    '      be choosePrinter() or defaultPrinter()
    '   -  Printing will not occur until closePage and closeDoc are executed
    '   -  closeDoc() cleans up in order to prevent resource leaks
    '
    '===================================================================================
    
    CLASS cTPrint
       INSTANCE hDlg AS LONG              'handle of window
       INSTANCE hdc AS LONG               'handle of a display device context 
       INSTANCE hFont AS LONG             'handle of current font
       INSTANCE hOrigFont AS LONG         'handle of original font
       INSTANCE charHt AS DOUBLE          'character height
       INSTANCE leftMargin AS DOUBLE      'left margin
       INSTANCE rightMargin AS DOUBLE     'right margin
       INSTANCE topMargin AS DOUBLE       'top margin
       INSTANCE bottomMargin AS DOUBLE    'bottom margin
       INSTANCE printerName AS STRING     'name of printer
       INSTANCE hDevMode AS LONG          'handle to DEVMODE structure
       INSTANCE origDuplex AS LONG        'original duplex mode
       INSTANCE origOrient AS LONG        'original orientation mode
       INSTANCE xPos, yPos AS DOUBLE      'default position for text print
    
       CLASS METHOD reportError(OPT BYVAL n AS LONG)
          LOCAL s AS STRING
          IF n = 1 THEN
             s = "Document printing error"
          ELSEIF n = 2 THEN
             s = "Page printing error"
          ELSE                        
             s = "Unspecified printer error"
          END IF
          MSGBOX s, %MB_ICONERROR, "Printer Error"
       END METHOD
       
       INTERFACE iTPrint
          INHERIT IUNKNOWN
          
          PROPERTY GET hDlg() AS LONG
             PROPERTY = hDlg
          END PROPERTY   
          
          PROPERTY GET hdc() AS LONG 
             PROPERTY = hdc
          END PROPERTY
          
          PROPERTY GET charHt() AS DOUBLE
             PROPERTY = charHt
          END PROPERTY
          
          METHOD defaultPrinter() AS STRING
             'determine default printer and device context handle
             LOCAL hPrinter, dwNeeded, n AS LONG
             LOCAL tm AS TEXTMETRIC, sz AS ASCIIZ*128
             LOCAL pDevMode AS DEVMODE PTR
             GetProfileString "WINDOWS", "DEVICE", "", sz, 127
             sz = TRIM$(PARSE$(sz, ",", 1)): printerName = sz
             OpenPrinter(sz, hPrinter, BYVAL %NULL) 'to obtain hPrinter
             dwNeeded = DocumentProperties(0, hPrinter, sz, BYVAL %NULL, BYVAL %NULL, 0)
             hDevMode = GlobalAlloc(%GHND, dwNeeded)
             pDevMode = GlobalLock(hDevMode)
             DocumentProperties 0, hPrinter, sz, BYVAL pDevMode, BYVAL %NULL, %DM_OUT_BUFFER 
             hdc = CreateDC("WINSPOOL", sz, BYVAL %NULL, BYVAL pDevMode)
             GetTextMetrics hdc, tm 
             n = tm.tmHeight + tm.tmExternalLeading 
             charHt = n / GetDeviceCaps(hDC, %LOGPIXELSY)
             METHOD = printerName
          END METHOD
    
          METHOD choosePrinter() AS STRING
             'choose printer and determine device context handle 
             LOCAL n AS LONG 
             LOCAL pd AS PRINTDLGAPI, pDevNames AS DEVNAMES PTR
             LOCAL tm AS TEXTMETRIC, psz AS ASCIIZ PTR
             pd.lStructSize = SIZEOF(pd)
             pd.Flags = %PD_RETURNDC OR %PD_HIDEPRINTTOFILE
             pd.Flags = pd.Flags OR %PD_ALLPAGES OR %PD_NOSELECTION OR %PD_NOPAGENUMS
             IF PrintDlg(pd) THEN 'call print dialog to select printer 
                pDevNames = GlobalLock(pd.hDevNames)
                psz = pDevNames + @pDevNames.wDeviceOffset: printerName = @psz
                GlobalUnlock pd.hDevnames: hdc = pd.hDC
             END IF 
             METHOD = printerName
             GetTextMetrics hdc, tm 
             n = tm.tmHeight + tm.tmExternalLeading
             charHt = n / GetDeviceCaps(hDC, %LOGPIXELSY)
             hDevMode = pd.hDevMode
          END METHOD
          
          METHOD newFont(fName AS STRING, fSize AS LONG, fAttr AS STRING) AS LONG
             'define a new font using a font name, font size in points, and font attributes
             'combine attributes: "b" for bold, "u" for underline, "i" for italic 
             LOCAL yppi, charSize, n AS LONG
             LOCAL wt, nBold, underline, italic AS DWORD, s AS STRING
             LOCAL tm AS TEXTMETRIC
             IF hFont THEN DeleteObject hFont
             IF hdc = 0 THEN ME.defaultPrinter
             IF LEN(fAttr) THEN
                nBold = SGN(INSTR(fAttr, "b"))*300
                underline = INSTR(fAttr,"u")
                italic = INSTR(fAttr,"i")
             END IF
             yppi  = GetDeviceCaps(hDC, %LOGPIXELSY)
             charSize = (fSize * yppi) \ 72
             wt = %FW_NORMAL + nBold
             hFont = CreateFont(-charSize, 0, 0, 0, wt, italic, underline, 0, _
                     %ANSI_CHARSET, %OUT_TT_PRECIS, %CLIP_DEFAULT_PRECIS, _
                     %DEFAULT_QUALITY, %FF_DONTCARE, BYCOPY fName)
             n = SelectObject(hdc, hFont)
             IF hOrigFont = 0 THEN hOrigFont = n
             GetTextMetrics hdc, tm 
             n = tm.tmHeight + tm.tmExternalLeading
             charHt = n / GetDeviceCaps(hDC, %LOGPIXELSY)
          END METHOD 
    
          METHOD getWidth(s AS STRING) AS DOUBLE
             'get the text width in inches 
             LOCAL cSize AS SIZEL
             IF hdc = 0 THEN ME.defaultPrinter
             GetTextExtentPoint32 hdc, BYVAL STRPTR(s), LEN(s), cSize
             METHOD = cSize.cx/GetDeviceCaps(hdc, %LOGPIXELSX)
          END METHOD
          
          METHOD truncate(s AS STRING, BYVAL wi AS DOUBLE) AS STRING
             'truncate the text to a specified width, in inches
             'if wi > 0, truncate the back end, else truncate the front end
             LOCAL nFit AS LONG, cSize AS SIZEL, ss AS STRING
             IF hdc = 0 THEN ME.defaultPrinter
             wi = GetDeviceCaps(hdc, %LOGPIXELSX) * wi 'width in pixels (logical units)
             IF wi > 0 THEN
                ss = s
                GetTextExtentExPoint hdc, BYVAL STRPTR(ss), LEN(ss), wi, nFit, BYVAL %NULL, cSize
                METHOD = LEFT$(ss, nFit)
             ELSEIF wi < 0 THEN
                wi = ABS(wi): ss = STRREVERSE$(s)
                GetTextExtentExPoint hdc, BYVAL STRPTR(ss), LEN(ss), wi, nFit, BYVAL %NULL, cSize
                METHOD = STRREVERSE$(LEFT$(ss, nFit))
             END IF
          END METHOD
       
          METHOD setMargins(nLeft AS DOUBLE, nRight AS DOUBLE, nTop AS DOUBLE, nBottom AS DOUBLE)
             'set page margins
             leftMargin = nLeft: rightMargin = nRight: topMargin = nTop: bottomMargin = nBottom
          END METHOD
          
          METHOD getLines(OPT BYVAL y AS DOUBLE) AS LONG
             'determine number of remaining lines from y to bottom margin
             LOCAL yppi, paperHt, topNoPrn, bottomNoPrn AS LONG
             LOCAL yn AS DOUBLE
             IF hdc = 0 THEN ME.defaultPrinter
             yppi = GetDeviceCaps(hdc, %LOGPIXELSY)
             paperHt = GetDeviceCaps(hdc, %PHYSICALHEIGHT)
             topNoPrn = GetDeviceCaps(hdc, %PHYSICALOFFSETY)
             bottomNoPrn = paperHt - topNoPrn - GetDeviceCaps(hdc, %VERTRES)
             y = MAX(topMargin, topNoPrn\yppi, y)
             yn = paperHt/yppi - MAX(bottomMargin, bottomNoPrn/yppi) - y 
             METHOD = INT(yn / charHt)
          END METHOD
    
          METHOD duplexPrint(n AS LONG)
             'n = 1 Simplex
             'n = 2 Horizontal
             'n = 3 Vertical
             '-------------------------------------------------
             LOCAL pDevMode AS DEVMODE PTR
             pDevMode = GlobalLock(hDevMode)
             IF origDuplex = 0 THEN origDuplex = @pDevMode.dmDuplex
             IF n AND @pDevMode.dmFields AND %DM_DUPLEX THEN
                @pDevMode.dmDuplex = n 
                @pDevMode.dmFields = @pDevMode.dmFields OR %DM_DUPLEX  
                ResetDC hDC, @pDevMode
                GlobalUnlock hDevMode
             END IF
          END METHOD 
          
          METHOD orientPrint(n AS LONG)
             'n = 1 Portrait
             'n = 2 Landscape
             '-------------------------------------------------
             LOCAL pDevMode AS DEVMODE PTR
             pDevMode = GlobalLock(hDevMode)
             IF origOrient = 0 THEN origOrient = @pDevMode.dmOrientation
             IF n AND @pDevMode.dmFields AND %DM_ORIENTATION THEN
                IF n THEN @pDevMode.dmOrientation = n 
                @pDevMode.dmFields = @pDevMode.dmFields OR %DM_ORIENTATION  
                ResetDC hDC, @pDevMode
                GlobalUnlock hDevMode
             END IF 
          END METHOD 
          
          METHOD setPos(x AS DOUBLE, y AS DOUBLE)
             xPos = x: yPos = y
          END METHOD
          
          METHOD printText(txt AS STRING, OPT BYVAL x AS DOUBLE, BYVAL y AS DOUBLE)
             'x, y are measured from the left and top edges of the paper
             'if x or y are omitted then last valuew, xLast, yLast are used
             LOCAL xc, yc, xm, leftNoPrn, rightNoPrn, topNoPrn, bottomNoPrn AS LONG 
             LOCAL paperWi, paperHt, xMax, yMax, xppi, yppi AS LONG
             LOCAL s AS STRING, sWidth AS DOUBLE, cSize AS SIZEL
             s = txt: IF x THEN xPos = x ELSE x = xPos
             IF y THEN yPos = y ELSE y = yPos + charHt: yPos = y
             IF hdc = 0 THEN ME.defaultPrinter
             paperWi = GetDeviceCaps(hdc, %PHYSICALWIDTH)
             paperHt = GetDeviceCaps(hdc, %PHYSICALHEIGHT)
             leftNoPrn = GetDeviceCaps(hdc, %PHYSICALOFFSETX)
             rightNoPrn = paperWi - leftNoPrn - GetDeviceCaps(hdc, %HORZRES)
             topNoPrn = GetDeviceCaps(hdc, %PHYSICALOFFSETY)
             bottomNoPrn = paperHt - topNoPrn - GetDeviceCaps(hdc, %VERTRES)
             GetTextExtentPoint32 hdc, BYVAL STRPTR(s), LEN(s), cSize
             xppi = GetDeviceCaps(hdc, %LOGPIXELSX): yppi = GetDeviceCaps(hdc, %LOGPIXELSY)
             xMax = paperWi - leftNoPrn - xppi * rightMargin
             yMax = paperHt - topNoPrn - yppi * bottomMargin
             xc = MAX(xppi * x - leftNoPrn, 0)
             yc = MAX(yppi * y - topNoPrn, 0)
             xm = MAX(xppi * leftMargin - leftNoPrn, 0)
             IF x < leftMargin THEN 'text overlaps left margin
                sWidth = ME.getWidth(s)
                s = ME.truncate(s, -(sWidth + x - leftMargin)): xc = xm 
             END IF 
             IF xc + cSize.cx > xMax THEN 'text overlaps right margin
                s = ME.truncate(s, (xMax - xc)/xppi)
             END IF
             IF y < topMargin THEN EXIT METHOD   'text overlaps top margin
             IF yc + yppi * charHt > yMax THEN EXIT METHOD  'text overlaps bottom margin
             TextOut(hdc, xc, yc, BYVAL STRPTR(s), LEN(s))
          END METHOD
    
          METHOD beginDoc(s AS STRING)
             LOCAL nErr AS LONG, sz AS ASCIIZ*64
             LOCAL di AS DOCINFO
             IF hdc = 0 THEN ME.defaultPrinter
             sz = s 
             di.cbSize = SIZEOF(di)
             di.lpszDocName = VARPTR(sz)
             nErr = StartDoc(hdc, di)
             IF nErr <= 0 THEN ME.reportError(1)
          END METHOD
          
          METHOD beginPage
             LOCAL nErr AS LONG
             nErr = StartPage(hdc)
             IF nErr <= 0 THEN ME.reportError(2)
          END METHOD
          
          METHOD closePage
             EndPage(hdc)
          END METHOD
          
          METHOD closeDoc
             origDuplex = 0
             origOrient = 0
             EndDoc(hdc)
             SelectObject hdc, hOrigFont
             DeleteObject hFont
             deleteDC hdc
          END METHOD
          
       END INTERFACE
    END CLASS
Working...
X