Midrange News for the IBM i Community


Posted by: Bob Cozzi
Rogue Programmer
Cozzi Productions, Inc.
Chicagoland
Epson TM88iii (or later) Print Driver in RPGIV
has no ratings.
Published: 01 Feb 2013
Revised: 01 Feb 2013 - 1607 days ago
Last viewed on: 27 Jun 2017 (3390 views) 

Using IBM i? Need to create Excel, CSV, HTML, JSON, PDF, SPOOL reports? Learn more about the fastest and least expensive tool for the job: SQL iQuery.

Print Graphic Images to IBM i attached Epson Printer Published by: Bob Cozzi on 01 Feb 2013 view comments

RPG IV Print Driver for EPSON TM-88

The Epson TM-88 is used in more Retail cash register applications than any other. However unlike PC-based operating systems, IBM i does not provide an SDK to build Printer Drivers.

To meet that challenge, we needed to write our own printer driver for the TM-88iii and later printers. The goal was to enable printing of graphic images (bitmaps) to the device so that the company logo, and customer signatures would print from RPG IV.

Note: I was working with EPSON on producing this, and since no one had done this type of thing before, they requested to include it in their developer library for other IBM i customers. I gave them permission to do so. But because of that, this is a single-stand-alone source member. It can be compiled and run directly without other tools or utilities. I did this to provide a single source member to EPSON without requiring them to also package up a bunch of /COPY members..

The program works by basically calling it from your RPG program (any RPG program) and passing in the IFS path where the BMP file is located. You can also pass the image in directly to the parameter if you've already read it into a variable (such as a BLOB) which is how I use it.The 2nd parameter is a flag that tells the program how the data is coming in, but default is assumes you're passing in the actual image bytes. To tell it you're passing in a file name, specify 1 for the 2nd parameter.

The EPSON only prints BMP files natively so if your images are stored in another format, just as JPG, you will have to convert them to BMP first, then pass them in. I expect the Image Transform API could be helpful to perform that conversion but I haven't needed to do that yet.

You also have to OVERRIDE the Print File (OVRPRTF) used in the code below to point at your Epson Print Device/OutQ.

The DDS for the EPSON88V print file description is as follows:

 EPSON88V PRTF Description

     A          R ASCIIPRT                                              
     A            DATA          56A        1 TRNSPY SPACEA(0) SPACEB(0) 
     A          R NORMALPRT                                             
     A            DATA          56A        1 SPACEA(1)                  

 You could do this without the DDS for the Printer file by sending the EPSON "transparency" code before every bit of data you send to the printer. But having both formats made sending graphic data and plain text much easier.

[I will update this post as I have time.]

RPG IV BMPTOEPSON Source Member

      **************************************************************************
      **  (c) 2011 by Bob Cozzi --  All rights reserved.                      **
      **  ------------------------------------------------------------------  **
      **  This program sends a BMP (bitmap) file to an Epson Printer directly
      **  from the host without using a PC-based program.
      **
      **  The concept:
      **  1) Map the BMP to the header and pixel array structures.
      **  2) Re-arrange the bmp pixel array bits as required by EPSON.
      **  3) Then send the ESC/Pos Store-Image command sequence to the printer.
      **  4) Then send the translated bmp pixel array to the printer.
      **  5) Then send the ESC/Pos "Print the image I just sent to you" command.
      **************************************************************************
     H DFTACTGRP(*NO) ACTGRP(*CALLER)
     H BNDDIR('QC2LE')
     H COPYRIGHT('(c) 2011 Robert Cozzi, Jr. All rights reserved.')
     FEPSON88V  O    E             PRINTER

     D BMPtoEPSON      PR                  EXTPGM('BMPTOEPSON')
     D  image                     32760A   Const Varying
     D  nFlags                       10U 0 Const OPTIONS(*NOPASS)

     D ADV_THE_PAPER   DS                  Qualified
     D  esc                           3u 0 Inz(27)
     D  escj                          3u 0 Inz(74)
     D  units                         3U 0 Inz(90)

     D CENTER_TEXT     DS                  Qualified
     D  esc                           3u 0 Inz(27)
     D  cmd                           3u 0 Inz(97)
     D  parm                          3U 0 Inz(1)

     D LEFT_TEXT       DS                  Qualified
     D  esc                           3u 0 Inz(27)
     D  cmd                           3u 0 Inz(97)
     D  parm                          3U 0 Inz(0)

     D RIGHT_TEXT      DS                  Qualified
     D  esc                           3u 0 Inz(27)
     D  cmd                           3u 0 Inz(97)
     D  parm                          3U 0 Inz(1)

     D PRINT_LINE_FEED...
     D                 DS                  Qualified
     D  esc                           3u 0 Inz(27)
     D  escd                          3u 0 Inz(100)
     D  lines                         3U 0 Inz(3)

     D asciiESC        DS                  Qualified
     D  code                          3U 0 Inz(3)
     D  cmdLength                     3U 0 Inz

     D crlf            C                   Const(X'0D0A')

     D xVal            S             10I 0
     D yVal            S             10I 0
     D xValMod         S             10I 0
     D xValFrame       S             10I 0
     D xValByte        S             10I 0
     D lastByte        S             10I 0
     D bitByte         S             10I 0
     D byteInc         S             10I 0
     D bmpImg          S          32760A   Varying
     D bmpImg2         S          32760A   Varying
     D bmpStrip        S            255A   Varying
     D idx             S             10I 0
     D iX              S             10I 0
     D i               S             10I 0

     D maxBytes        S             10I 0
     D nullValues      S             64A   Inz(*ALLX'00')
     D szOutput        S             56A   Varying
     D szDataOut       S          32760A   Varying
     D ptr             S               *

     D tiny            DS                  Qualified
     D  value                         2A   Inz(X'0000')
     D  left                          1A   overlay(value:1)
     D  right                         1A   overlay(value:2)
     D  char                          1A   overlay(value:2)
     D  int                           5I 0 overlay(value)
     D  uint                          5U 0 overlay(value)

     D LittleEndianShort_T...
     D                 DS                  Qualified Inz
     D  Value                         5U 0
     D  L                             3U 0 Overlay(value)
     D  H                             3U 0 Overlay(value:*NEXT)

     D BigEndianShort_T...
     D                 DS                  Qualified Inz
     D  Value                         5U 0
     D  H                             3U 0 Overlay(value)
     D  L                             3U 0 Overlay(value:*NEXT)

     D littleEndianLong_T...
     D                 DS                  Qualified Inz
     D  Value                        10U 0
     D  L                             5U 0 Overlay(value)
     D  LL                            3U 0 Overlay(L)
     D  LH                            3U 0 Overlay(L:*NEXT)
     D  H                             5U 0 Overlay(value:*NEXT)
     D  HL                            3U 0 Overlay(H)
     D  HH                            3U 0 Overlay(H:*NEXT)

     D bigEndianLong_T...
     D                 DS                  Qualified Inz
     D  Value                        10U 0
     D  H                             5U 0 Overlay(Value)
     D  HH                            3U 0 Overlay(H)
     D  HL                            3U 0 Overlay(H:*NEXT)
     D  L                             5U 0 Overlay(value:*NEXT)
     D  LH                            3U 0 Overlay(L)
     D  LL                            3U 0 Overlay(L:*NEXT)

     D escPrintImgV    DS                  Qualified
     D  GS                            3U 0 Inz(29)
     D  lParen                        3U 0 Inz(40)
     D  L                             3U 0 Inz(76)
     D  Pl                            3U 0 Inz(2)
     D  Ph                            3U 0 Inz(0)
     D  M                             3U 0 Inz(48)
     D  Fn                            3U 0 Inz(50)

     D escLoadImgV     DS                  Qualified
     D  GS                            3U 0 Inz(29)
     D  lParen                        3U 0 Inz(40)
     D  L                             3U 0 Inz(76)
     D  Pl                            3U 0 Inz
     D  Ph                            3U 0 Inz
     D  M                             3U 0 Inz(48)
     D  Fn                            3U 0 Inz(112)
     D  A                             3U 0 Inz(48)
        //  BX and BY are multipliers for signature size/scale (I think)
        //  Values are: BX(1) size is 180px wide and BX(2) size is 360px wide
     D  Bx                            3U 0 Inz(1)
     D  By                            3U 0 Inz(1)
     D  C                             3U 0 Inz(49)
     D  XL                            3U 0 Inz(0)
     D  XH                            3U 0 Inz(0)
     D  YL                            3U 0 Inz(0)
     D  YH                            3U 0 Inz(0)

        // Load Bitmap image using TM-88II/III ESC/Pos commands
     D escLoadImgII    DS                  Qualified
     D  GS                            3U 0 Inz(29)
     D  asterisk                      3U 0 Inz(42)
     D  X                             3U 0 Inz(0)
     D  Y                             3U 0 Inz(0)

     D escPrintImgII   DS                  Qualified
     D  GS                            3U 0 Inz(29)
     D  forwardSlash                  3U 0 Inz(47)
     D  M                             3U 0 Inz(0)

     D escLoadImgIINV  DS                  Qualified
     D  FS                            3U 0 Inz(28)
     D  q                             3U 0 Inz(113)
     D  n                             3U 0 Inz(12)
     D  XL                            3U 0 Inz(0)
     D  XH                            3U 0 Inz(0)
     D  YL                            3U 0 Inz(0)
     D  YH                            3U 0 Inz(0)
     D escPrintImgIINV...
     D                 DS                  Qualified
     D  FS                            3U 0 Inz(28)
     D  p                             3U 0 Inz(112)
     D  n                             3U 0 Inz(12)
     D  m                             3U 0 Inz(0)



     D BMP_Struct_T    DS                  Qualified Inz
     D  hdr                                LikeDS(BitmapFileHeader)
     D  info                               LikeDS(BitmapInfoHeader)
     D***data                     32765A

     D BITMAPfileHeader...
     D                 DS                  Qualified Inz
     D  magicID                       2A
     D  file_Size                    10U 0
     D  reserved1                     5U 0
     D  reserved2                     5U 0
     D  image_Offset                 10U 0

     D BITMAPinfoHeader...
     D                 DS                  Qualified Inz
     D  info_Size                    10u 0
     D  width                        10u 0
     D  height                       10u 0
     D  nWidth                       10i 0 Overlay(width)
     D  nHeight                      10i 0 Overlay(height)
     D  nPlanes                       5u 0
     D  bitsPerPixel                  5u 0
     D  compress_type                10u 0
     D  Image_Size                   10u 0
     D  XPixsPerMeter                10u 0
     D  yPixsPerMeter                10u 0
     D  nColors                      10u 0
     D  nImpColors                   10u 0

     D loWord          PR             3U 0
     D  shortValue                    5U 0 Const
     D hiWord          PR             3U 0
     D  shortValue                    5U 0 Const

     D LtoBShort...
     D                 PR             5U 0
     D  shortValue                    5U 0 Const

     D LtoBLong...
     D                 PR            10U 0
     D  longValue                    10U 0 Const

     D cvtBmpHdr       PR

     D reverse         PR
     D  ptr                            *   Value
     D  len                          10I 0 Const
     D  XORBits                       1N   Const OPTIONS(*NOPASS)

     D LoadFromFile    PR            10I 0
     D   bmpData                  32760A   Varying
     D   ifsFile                   2000A   Const Varying

     D bmp             DS                  LikeDS(bmp_Struct_T) Inz
     D bmpData         S          32760A   Varying

     D BMPtoEPSON      PI
     D  image                     32760A   Const Varying
     D  nFlags                       10U 0 Const OPTIONS(*NOPASS)

     D align           S             56A   Varying
     D flags           S             10U 0

     D BMP_FLAGS       DS                  Qualified
     D  FILE                         10I 0 Inz(1)
     D  ALIGNLEFT                    10I 0 Inz(2)
     D  ALIGNRIGHT                   10I 0 Inz(4)
     D  ALIGNCENTER                  10I 0 Inz(8)
     D  TM88II                       10I 0 Inz(16)
     D  TM88IINV                     10I 0 Inz(32)
     D  LEFT                         10I 0 OVERLAY(AlignLeft)
     D  RIGHT                        10I 0 OVERLAY(AlignRight)
     D  CENTER                       10I 0 OVERLAY(AlignCenter)
     D  TM88III                      10I 0 OVERLAY(TM88II)
     D  TM88IIINV                    10I 0 OVERLAY(TM88IINV)

     C                   MOVE      *ON           *INLR
      /free
           if (%Parms()>=2);
              flags = nFlags;
           endif;
           if (((%Parms()>=2) and
                %BITAND(nFlags:BMP_FLAGS.FILE)=BMP_FLAGS.FILE) or
               (%subst(image:1:2) <> X'424D') );
                 LoadFromFile(bmpData : image);  // Load it from the IFS
           else;          //  Otherwise, assume the image data is passed in.
              bmpData = image;
           endif;

           bmp = %subst(bmpData:1:%size(bmp_Struct_T));

           if (bmp.hdr.magicID <> X'424D'); // Must = ascii('BM')
              return;
           endif;

           cvtBmpHdr(); // Convert BMP header from little-endian to BIG-Endian
                        // Extract the BMP pixel array
           bmpData = %subst(bmpData:bmp.hdr.image_Offset:bmp.info.image_size);

           xVal = bmp.info.width;
           yVal = bmp.info.height;
           xValMod = %rem(xVal : 32);
           XValFrame = %DIV(xVal + (32 - xValMod)       : 8);
           xValByte  = %DIV(XVal + (8 - %REM(xVal : 8)) : 8);
           bitByte = %REM(xVal : 8);

           // If you can read and understand the follow loop/conversion
           // you are a genious. It converts the bits/bytes of a bmp
           // image file's pixel array to what is needed by an
           // Epson TM series printer. That is, it flips the bits around,
           // gets rid the the padding, and then reverses (xor) the bits.
           // This allows the BMP to be printed directly from RPG IV.
           //  You're welcome!

           iX = 1;
           for idX = 1 to yVal;
              if (ix < %len(bmpData));
                 if (ix + (xValByte-1) > %len(bmpData) );
                    bmpStrip = %subst(bmpData : ix);
                 else;
                    bmpStrip = %subst(bmpData : iX : xValByte);
                 endif;
                 tiny.uint = 0;
                 lastByte  = 0;
                 if (bitByte > 0);
                     tiny.uint = 0;
                     tiny.char = %subst(bmpStrip:%len(bmpStrip));
                   if (bitByte = 1);
                      lastByte  = tiny.uint + 127;
                   elseif (bitByte = 2);
                      lastByte  = tiny.uint + 63;
                   elseif (bitByte = 3);
                     lastByte  = tiny.uint + 31;
                   elseif (bitByte = 4);
                     lastByte  = tiny.uint + 15;
                   elseif (bitByte = 5);
                     lastByte  = tiny.uint + 7;
                   elseif (bitByte = 6);
                     lastByte  = tiny.uint + 3;
                   elseif (bitByte = 7);
                     lastByte  = tiny.uint + 1;
                   endif;
                 endif;

                 if (bitByte > 0);
                    tiny.uint = lastByte;
                 endif;

                 reverse(%addr(bmpStrip) + 2 : %len(bmpStrip):*ON);

                 if ((xValByte-1) > %len(bmpStrip));
                    bmpImg += bmpStrip;
                 else;
                    if (bitByte = 0);
                       bmpImg += %subst(bmpStrip : 1  : xValByte-1) ;
                    else;
                       bmpImg += tiny.char + %subst(bmpStrip :1: xValByte-1);
                    endif;
                 endif;
              endif;
              iX += xValFrame;
           endfor;

           reverse(%addr(bmpImg)+2 : %len(bmpImg)); // Flip the image over

           align = '';
           if (%Parms() >= 2);  // Flags specified?
              if     (%bitAND(nFlags:BMP_FLAGS.LEFT )=BMP_FLAGS.LEFT);
                  align = LEFT_TEXT;
              elseif (%bitAND(nFlags:BMP_FLAGS.RIGHT)=BMP_FLAGS.RIGHT);
                  align = RIGHT_TEXT;
              elseif (%bitAND(nFlags:BMP_FLAGS.CENTER)=BMP_FLAGS.CENTER);
                  align = CENTER_TEXT;
              endif;
           endif;
           if  ( align = '');
              align = LEFT_TEXT;
           endif;
           data = align + nullValues;
           write ASCIIPRT; // Try to center the signature on the page

           if (%BITAND(flags : BMP_FLAGS.TM88ii)=BMP_FLAGS.TM88ii);  // Use TM-88II/III esc codes?
              escLoadImgii.x = xVal/8;
              escLoadImgii.y = yVal/8;
              szDataOut = escLoadImgii;
              szDataOut += bmpImg;  // Add the img to the output buffer
              szDataOut += escPrintImgii;
           elseif (%BITAND(flags : BMP_FLAGS.TM88iiNV)=BMP_FLAGS.TM88iiNV);
              escLoadImgiiNV.xL = loWord( xVal );
              escLoadImgiiNV.xH = hiWord( xVal );
              escLoadImgiiNV.yL = loWord( yVal );
              escLoadImgiiNV.yH = hiWord( yVal );
              szDataOut = escLoadImgiiNV;
              szDataOut += bmpImg;
              szDataOut = escPrintImgiiNV;
           else;    // Default is to use TM-88V Esc/Pos codes
              escLoadImgV.pL = loWord(%len(bmpImg)+10);
              escLoadImgV.pH = hiWord(%len(bmpImg)+10);
              escLoadImgV.xL = loWord( xVal );
              escLoadImgV.xH = hiWord( xVal );
              escLoadImgV.yL = loWord( yVal );
              escLoadImgV.yH = hiWord( yVal );
              szDataOut = escLoadImgV;
              szDataOut += bmpImg;  // Add the img to the output buffer
              szDataOut += escPrintImgV;
           endif;

               // Write the data to the Epson ASCII Printer
           maxBytes = %size(szOutput)-2;
           dow (szDataOut <> '');
              if (%len(szDataOut) <= maxBytes);
                 if (%len(szDataOut) > 0);
                    szOutput = szDataOut;
                 else;
                    szOutput = '';
                 endif;
                 szDataOut = '';
              else;
                 szOutput  = %subst(szDataOut:1: maxBytes);
                 szDataOut = %subst(szDataOut : %Len(szOutput)+1);
              endif;
              if (%len(szOutput) > 0);
                 data = szOutput;
                 if (%len(szOutput) < %len(data));
                 // data = %trimR(data) + nullValues;
                 endif;
                 write ASCIIPRT;
              endif;
           enddo;
           //  PRINT_LINE_FEED.lines = 5;
           //  data = PRINT_LINE_FEED + LEFT_TEXT + nullValues;
           //  write ASCIIPRT;
           return;
      /end-free

     P LtoBLong...
     P                 B
     D LtoBLong...
     D                 PI            10U 0
     D  longValue                    10U 0 Const

     D le              DS                  LikeDS(LittleEndianLong_T) Inz
     D be              DS                  LikeDS(BigEndianLong_T) Inz
      /free
          le.value = longValue;
          be.ll = le.ll;
          be.lh = le.lh;
          be.hl = le.hl;
          be.hh = le.hh;
          return be.value;
      /end-free
     P LtoBLong...
     P                 E
     P LtoBShort...
     P                 B
     D LtoBShort...
     D                 PI             5U 0
     D  shortValue                    5U 0 Const

     D le              DS                  LikeDS(LittleEndianShort_T) Inz
     D be              DS                  LikeDS(BigEndianShort_T) Inz
      /free
          le.value = shortValue;
          be.l = le.l;
          be.h = le.h;
          return be.value;
      /end-free
     P LtoBShort...
     P                 E

     P hiWord          B
     D hiWord          PI             3U 0
     D  shortValue                    5U 0 Const

      **************************************************
      **  Returns the HIword from a
      **  a 2-byte big-endian integer value.
      **************************************************
     D short           DS                  Qualified
     D  value                         5U 0
     D  hiWord                        3U 0 Overlay(value)
     D  loWord                        3U 0 Overlay(value:*NEXT)
      /free
          short.value = shortValue;
          return short.hiWord;
      /end-free
     P hiWord          E


     P loWord          B
     D loWord          PI             3U 0
     D  shortValue                    5U 0 Const

      **************************************************
      **  Returns the LOword from a
      **  a 2-byte big-endian integer value.
      **************************************************
     D short           DS                  Qualified
     D  value                         5U 0
     D  hiWord                        3U 0 Overlay(value)
     D  loWord                        3U 0 Overlay(value:*NEXT)
      /free
          short.value = shortValue;
          return short.loWord;
      /end-free
     P loWord          E

     P cvtBmpHdr       B
     D cvtBmpHdr       PI
     D litEnd          DS                  LikeDS(littleEndianLong_T)  Inz
     D bigEnd          DS                  LikeDS(BigEndianLong_T)  Inz
      /free
           bmp.hdr.image_Offset = LtoBLong(bmp.hdr.image_Offset);
           bmp.hdr.file_size    = LtoBLong(bmp.hdr.file_size);

           bmp.info.info_size = LtoBLong(bmp.info.info_size);

           litEnd.value = bmp.info.image_size;
           bmp.info.image_size  = LtoBLong(bmp.info.image_size);
           bigEnd.value = bmp.info.image_size;
           bmp.info.Width       = LtoBLong(bmp.info.Width);
           bmp.info.height      = LtoBLong(bmp.info.height);

           bmp.info.compress_Type = LtoBLong(bmp.info.compress_Type);
           bmp.info.XPixsPerMeter = LtoBLong(bmp.info.XPixsPerMeter);
           bmp.info.YPixsPerMeter = LtoBLong(bmp.info.YPixsPerMeter);
           bmp.info.nColors       = LtoBLong(bmp.info.nColors);
           bmp.info.nImpColors    = LtoBLong(bmp.info.nImpColors);
           bmp.info.nPlanes       = LtoBShort(bmp.info.nPlanes);
           bmp.info.bitsPerPixel  = LtoBShort(bmp.info.bitsPerPixel);

      /end-free
     P cvtBmpHdr       E

     P reverse         B
     D reverse         PI
     D  ptr                            *   Value
     D  len                          10I 0 Const
     D  XORBits                       1N   Const OPTIONS(*NOPASS)
     D start           S               *
     D end             S               *
     D strEnd          S              1A   Based(end)
     D strStart        S              1A   Based(start)
      /free
           start = ptr;
           end = ptr + (len-1);
           dow ( start < end );
                   // Flip (reverse-order) the bytes in the input string
             strStart = %BITXOR(strStart : strEnd);
             strEnd   = %BITXOR(strEnd   : strStart);
             strStart = %BITXOR(strStart : strEnd);
                   // Need to xor the bits?
             if (%parms() >= 3 and XORbits);
                strStart = %BITXOR(strStart : X'FF');
                strEnd   = %BITXOR(strEnd   : X'FF');
             endif;
             start += 1;
             end   -= 1;
           enddo;
          return;
      /end-free
     P reverse         E

     P LoadFromFile    B
     D LoadFromFile    PI            10I 0
     D   bmpData                  32760A   Varying
     D   ifsFile                   2000A   Const Varying

     D O_RDONLY        C                   Const(1)

     D openIFS         PR            10I 0 extProc('open64')
     D  szIFSFileName                  *   Value options(*string)
     D  openFlags                    10I 0 Value
     D  fMod                         10U 0 Value options(*nopass)
     D  CCSID                        10U 0 Value options(*nopass)

     D readIFS         PR            10I 0 ExtProc('read')
     D  hFile                        10I 0 Value
     D  szBuffer                       *   Value
     D  nBufLen                      10U 0 Value

     D closeIFS        PR            10I 0 ExtProc('close')
     D  hFile                        10I 0 Value
     D imgBuffer       S          32760A
     D nBytes          S             20I 0
     D hFile           S             10I 0
      /free

           hFile = openIfs(%trimR(ifsFile):  O_RDONLY );
           if (hFile < 0);
             return 0;
           endif;
           nBytes = readIfs(hFile:%addr(imgBuffer): %size(imgBuffer));
           if (nBytes < 0);
             return 0;
           endif;
           bmpData = %subst(imgBuffer:1:nBytes);
           closeIFS(hFile);
           return nBytes;
      /end-free
     P LoadFromFile    E 

 

Return to midrangenews.com home page.
Sort Ascend | Descend

COMMENTS