Download self extracting .EXE file
This is the .txt file from the package above.

All the BMP functions and subs below that work on BMPs assume that the original
BMP is ATTACHed.

The following 4 functions can either work on the same copy of the BMP or, upon
reqest, create a new copy. If/When a new copy is created that copy is ATTACHED
and its handle is retruned. If no new copy is requested then ZERO is returned.
  BMP_RECOLOR    : Exchanges one color for another in either a new
  BMP_FLIP       : Flips the BMP top to bottom (This is NOT the same as ROTATE_180
  BMP_MIRROR     : Flips the BMP left to right
  BMP_ROTATE_180 : This function is an exception to all the other rotate functions

The ROTATE functions (except 180) all create new BMPs and return that handle. The
original may be retained or discarded as requested. They all rotate the image
clockwise around the center and the new image is sized to fit the whole of the
new image. (no clipping)
  BMP_ROTATE_90  : the top becomes the right side
  BMP_ROTATE_270 : the top becomes the left side
  BMP_ROTATE     : # of degrees clockwise

These color functions work on the RGB and/or BGR (as indicated)
  COLOR_TO_RGB   : returns the 3 elements of the RGB color sent
  COLOR_TO_BGR   : returns the 3 elements of the BGR color sent
  COLOR_LIGHTEN  : attempts to return a new color value P% lighter than that sent
  COLOR_DARKEN   : attempts to return a new color value P% darker than that sent
                   The darken and lighten functions are imperfect and some playing
                   with the original values may be required.

  BMP_ADD_BORDER : creates a new BMP and positions the old BMP within the new one at
                   the position requested

  BMP_RESIZE     : creates a new BMP of a given size, ATTACHes it, and copies the
                   original image using COPY STRETCH into the new BMP
                   the original BMP may be retained or discared as requested
  BMP_BOX_TT     : draws a two tone box at the coordinates given using the 2 color
                   parameters sent
                   Tcolor is used top & left Bcolor is used left and botom
  BMP_BUTTONIZE  : creates a new BMP, draws a 3D border in the new area, and copies
                   the origial into the center.
                   the original BMP may be retained or discared as requested
  BMP_Calendar   : creates a calendar of one month in a new BMP or upon an exisitng BMP
                   SEE: XPRN_CalendarTYPE for required details
                   If a new BMP is created the background is %WHITE else the background
                   is transparent.

  GPRN_ALIGN     : prints text using the current font cliped into a provided width either
                   left, center, or right justified
                   Align = 0 Left   : Printing starts at Xpos! continues for Wide!
                           1 Center : Printing is centered at Xpos! and is limited to Wide!
                           2 Right  : Printing ends at Xpos! and is limited to Wide!
                   Wide!            : If ZERO then printing is done to BMP Client boundarys
                   RETURNS: The number of characters printed.
  XPRRIN_TO_BMP  : Computes the factor between the screen's DPI and the currently attached
                   printer's DPI. The return is an 8 byte string that fits the POINTFLOAT UDT

XPRN_ATTACH
  Attempts to open/attach the specified printer or defalut printer if no printer 'name' is
  sent.
  Printer$ can be "DEFAULT", "CHOOSE", The specific name of the device or NUL$
  If Printer$ is NUL then "CHOOSE" is selected
  RETURNS: > 0 if successful
             0 if user canceled
           < 0 if an error occured (-ErrClear)
  NOTE: the return of an error message depends greatly upon the printer driver and not all
        drivers will return an error 68 (Device unavailable).

XPRN_ErrorMSG
  This is an internal function.

XPRN_OPEN XPptr AS XPRN_SetupTYPE PTR
  Attaches printer
  Sets internal variable
  Sets Quality
  Sets orientation
  Sets quality
  Sets scale
  Sets font & color
  RETURNS: SEE XPRN_ATTACH

  USES:
    tXP.Printer
    tXP.Font
    tXP.UoM

  SETS:
    tXP.AvgChr
    tXP.Page
    tXP.CurX, tXP.CurY

XPRN_SET_SCALE ( BYVAL UnitSize AS LONG )
  0 = Pixels
  1 = centemeters
  2 = inches

  SETS:
    tXP.UoM
    tXP.AvgChr
    tXP.Client

XPRN_BLANKPAGE ()
  Forces ejection of blank page on older printers

XPRN_GET_SETUP ()
  Returns pointer to current global XPRN_SetupTYPE

XPRN_SET_SETUP tXprn AS XPRN_SetupTYPE PTR
  Sets global XPRN_SetupTYPE to to tXPrn

XPRN_SET_FONT  FontName$, Pts&, Style&
  SETS:
    tXP.AvgChr

XPRN_SET_MARGINS Lft!, Top!, Rgt!, Btm!
  Actual marging values are computed taking in account of the printer's printable area
  and are set accordingly.
  SETS:
    tXP.Margin

XPRN_ALIGN Txt$, Align&, Xpos!, Ypos! [,Wide!]
  Align = 0 Left   : Printing starts at Xpos! continues for Wide!
          1 Center : Printing is centered at Xpos! and is limited to Wide!
          2 Right  : Printing ends at Xpos! and is limited to Wide!
  Wide!            : If ZERO then printing is done to tXP.Client boundarys
  RETURNS: The number of characters printed.
  SETS:
    Nothing

XPRN_SET_TABS TabTxt$
  There are 3 tab types that match the alignments above. Each tab within the
  incoming string has 3 parts: Alignment, Xpos! offset, width of tab space.
  Each element is separated with a comma. Each tab set is separated with a
  pipe "|" and any number of tabs (up to 30) can be set.
  EG: XPRN_SET_TABS("L,1.2,3.4|C,3.4,4.1|R,4.1,3") ' tXP.TabCnt = 3 as 3 tabs are set
      1st tab: Left alignment starting at 1.2units for a width of 3.4units
      2ed tab: Center alignment starting at 3.4units for a width of 4.1 units
      3rd tab: Right alignment starting at 4.1units for a width of 3units
  NOTE: If the alignment character "C" or "R" is in lower case then
        the Xpos! value being sent is the left most boundry and the required Xpos! value
        for XPRN_ALIGN is computed based on the width
  SEE: XPRN_ALIGN

  SETS:
    tXP.TabCnt
    tXP.Tab() elements

XPRN_TAB_TEXT Txt$, Ypos! [,TabTxt$]
  Txt$    is a string of the fields to be printed that match the tab setup.
          Each field within the string will be delimited by the $TAB character.
  Ypos!   is the Y position of the page to be printed at
  TabTxt$ If not empty then it is sent to XPRN_SET_TABS

XPRN_WRAP Txt$, Xpos!, Ypos! [,Lhigh!] [, Wide!] [[, High!] [[[, Parser$]]]
    Txt$    = the block of text to be printed
    Xpos!   = the left most position to print
    Ypow!   = starting top most position to print
    Lhigh!  = line heigth if 0 then character heigth is used
    Wide!   = width of text block to be printed : if 0 then right most column of page is used
    High!   = height of text block to be printed : if 0 then bottom most row of page is used
    Parser$ = whatever character or characters used to separate paragraphs
              if NUL then $CRLF is assumed
    RETURN VALUE is the portion of Txt$, if any, that did not fit into the area assigned.

XPRN_LABEL_CLOSE
    Closes the printer & resets the interanl UDT

XPRN_LABEL_OPEN  tLbl AS XPRN_LabelTYPE PTR
    USES: tLbl
          tLbl
    SETS: tLbl.Printer
          And all measurements
 RETURNS:  0 if no errors
          -1 if user aborts
          ERR if an error occured

XPRN_LABEL Txt$
  Prints starting at tLB.LabelNo for tLB.Copies
  UPDATES: tLB.LabelNo

XPRN_LABEL_SETUP  hParent???, tLBL AS XPRN_LabelTYPE PTR
  Dialog to allow user setup for labeling
  RETURNS: %False if user aborted the setup

XPRN_CALENDAR tC AS XPRN_CalendarTYPE
  Prints a calendar for one month

Circle_Point Lft!, Top!, Rgt!, Btm!, Angle!
  Computes the required XY position to place a point on a circle that is
  bounded by the incoming rectangle at the angle of Angle!

  RETURNS   An 8 byte string that fits into the POINTFLOAT UDT or
            X! = CVS(Rslt$,1)
            Y! = CVS(Rslt$,5)

Poly_Points tP AS PolygonTYPE, OPT BYVAL PGptr???
  Computes the required points of a polygon of, at least, 3 points and either
    tP       UDT to carry info to and from Poly_Points
    PGptr??? pointer to a Polygons UDT
    RETURNS: IF PGptr??? <> 0 then the UDT is filled
    RETURNS: IF PGptr??? =  0 then a string that can be placed into a PolyPoints
    NOTE:    Each point of the polygon requrirs 8 bytes of storage space
    SEE:     GRAPHIC POLYGON
             Demo Polygons.bas

Star_Points
  Computes the required points of a start of, at least, 3 points and either
    tP1      UDT defining the outside (larger) box defining the star
    tP2      UDT defining the inside (smaller) box defining the star
             tP2.Pnts is ignored and only tP1.Pnts is used
             If tP2.FirstDeg < 0 then the starting point is computed to
               create an equal-lateral star
    PGptr??? pointer to a Polygons UDT
    RETURNS: IF PGptr??? <> 0 then the UDT is filled
    RETURNS: IF PGptr??? =  0 then a string that can be placed into a PolyPoints
    NOTE:    Each point of the star requires 16 bytes of storage space
    SEE:     GRAPHIC POLYGON
             Demo Polygons.bas

Poly_Ready
  Is used internally by the 2 routines above to make the initial computations