Convert Zebra .GRF Hex to .BMP
Recently I needed to print a Zebra-formatted shipping label to a SATO printer. I have another program that transforms ZPL II to SATO but had not implemented graphics because there was no need.
Until now.
Below is RPGLE source that translates Zebra’s .GRF hex string to a .BMP string that can be used in SATO’s GM command and streamed to the SATO printer for printing. It’s implemented as a stand-alone program but can easily be changed to a procedure for inclusion in a *SRVPGM.
h dftactgrp(*no)
d pTotBytes s 7p 0
d pBytesPerRow s 7p 0
d pHexIn s 1000000a ccsid(65535)
d pBmpOut s 1000000a ccsid(65535)
d pBmpSize s 7p 0
d div s 10i 0
d rem s 10i 0
d rows s 10i 0
d pad s 4a varying ccsid(65535)
d i# s 10i 0
d j# s 10i 0
d dsHeader ds template
d id 2a ccsid(819)
d size 10u 0
d rsv1 5i 0
d rsv2 5i 0
d offset 10u 0
d dsDIB ds template
d sizeThisHdr 10u 0
d bmpWidthPx 10i 0
d bmpHeightPx 10i 0
d colorPlane# 5u 0
d pxBits 5u 0
d compress 10u 0
d sizeImg 10u 0
d hRes 10i 0
d vRes 10i 0
d color#Pal 10u 0
d color#Imp 10u 0
d dsBmp ds qualified inz
d header likeds(dsHeader)
d dib likeds(dsDIB)
d colorTbl 8a ccsid(65535)
d img 999360a ccsid(65535)
// The IBM i uses Big Endian. Windows uses Little Endian.
// These routines convert BE to LE:
d LE4i pr 10i 0
d pInt 10i 0 value
d LE2i pr 5i 0
d pInt 5i 0 value
d LE4u pr 10u 0
d pUint 10u 0 value
d LE2u pr 5u 0
d pUint 5u 0 value
c *entry plist
c parm pTotBytes
c parm pBytesPerRow
c parm pHexIn
c parm pBmpOut
c parm pBmpSize
*inlr = *on;
pBmpOut = *allx'00';
pBmpSize = 0;
clear dsBmp;
if pBytesPerRow = 0;
return;
endif;
rows = pTotBytes / pBytesPerRow;
// image rows must be on dword boundary. Pad with x'00':
div = %div(pBytesPerRow: 4);
rem = %rem(pBytesPerRow: 4);
select;
when rem = 1;
pad = x'000000';
when rem = 2;
pad = x'0000';
when rem = 3;
pad = x'00';
other;
pad = '';
endsl;
rem = %len(pad);
// Ensure pBmpOut won't overflow:
pBmpSize = ((pBytesPerRow + rem) * rows) +
%size(dsHeader) +
%size(dsDIB) +
%size(dsBmp.colorTbl);
if pBmpSize > %size(pBmpOut);
pBmpSize = 0;
return;
endif;
dsBmp.header.id = 'BM';
dsBmp.header.size = LE4u(pBmpSize);
dsBmp.header.offset = LE4u(%size(dsBmp) - %size(dsBmp.img));
dsBmp.dib.bmpWidthPx = LE4i(pBytesPerRow * 8);
dsBmp.dib.bmpHeightPx = LE4i(rows);
dsBmp.dib.sizeThisHdr = LE4u(%size(dsBmp.DIB));
dsBmp.dib.colorPlane# = LE2u(1);
dsBmp.dib.pxBits = LE2u(1);
dsBmp.dib.compress = 0;
dsBmp.dib.sizeImg = 0;
dsBmp.dib.hRes = 0;
dsBmp.dib.vRes = 0;
dsBmp.dib.color#Pal = 1;
dsBmp.dib.color#Imp = 0;
dsBmp.colorTbl = x'00000000FFFFFF00'; // monochrome
// .BMP files are read from the bottom up. This routine writes
// dsBmp.img bytes starting from the end:
i# = pTotBytes - pBytesPerRow + 1;
j# = 1;
dow i# > 0;
%subst(dsBmp.img: j#: pBytesPerRow + rem) =
%bitnot(%subst(pHexIn: i#: pBytesPerRow) + pad);
i# -= pBytesPerRow;
j# += pBytesPerRow + rem;
enddo;
%subst(pBmpOut: 1: pBmpSize) = dsBmp;
return;
*----------------------------------------------------------
p LE4i b
d LE4i pi 10i 0
d pInt 10i 0 value
d dsBigEnd ds qualified inz
d int 10i 0
d byte1 1a overlay(int: 1)
d byte2 1a overlay(int: *next)
d byte3 1a overlay(int: *next)
d byte4 1a overlay(int: *next)
d dsLitEnd ds qualified inz
d int 10i 0
d byte4 1a overlay(int: 1)
d byte3 1a overlay(int: *next)
d byte2 1a overlay(int: *next)
d byte1 1a overlay(int: *next)
dsBigEnd.int = pInt;
eval-corr dsLitEnd = dsBigEnd;
return dsLitEnd.int;
p LE4i e
*----------------------------------------------------------
p LE2i b
d LE2i pi 5i 0
d pInt 5i 0 value
d dsBigEnd ds qualified inz
d int 5i 0
d byte1 1a overlay(int: 1)
d byte2 1a overlay(int: *next)
d dsLitEnd ds qualified inz
d int 5i 0
d byte2 1a overlay(int: *next)
d byte1 1a overlay(int: *next)
dsBigEnd.int = pInt;
eval-corr dsLitEnd = dsBigEnd;
return dsLitEnd.int;
p LE2i e
*----------------------------------------------------------
p LE4u b
d LE4u pi 10u 0
d pUint 10u 0 value
d dsBigEnd ds qualified inz
d uint 10u 0
d byte1 1a overlay(uint: 1)
d byte2 1a overlay(uint: *next)
d byte3 1a overlay(uint: *next)
d byte4 1a overlay(uint: *next)
d dsLitEnd ds qualified inz
d uint 10u 0
d byte4 1a overlay(uint: 1)
d byte3 1a overlay(uint: *next)
d byte2 1a overlay(uint: *next)
d byte1 1a overlay(uint: *next)
dsBigEnd.uint = pUint;
eval-corr dsLitEnd = dsBigEnd;
return dsLitEnd.uint;
p LE4u e
*----------------------------------------------------------
p LE2u b
d LE2u pi 5u 0
d pUint 5u 0 value
d dsBigEnd ds qualified inz
d uint 5u 0
d byte1 1a overlay(uint: 1)
d byte2 1a overlay(uint: *next)
d dsLitEnd ds qualified inz
d uint 5u 0
d byte2 1a overlay(uint: *next)
d byte1 1a overlay(uint: *next)
dsBigEnd.uint = pUint;
eval-corr dsLitEnd = dsBigEnd;
return dsLitEnd.uint;
p LE2u e
*----------------------------------------------------------