Question Read File (img) Binary

bigwill

Member
Hi all

I have a problem that mr. Google can't help me with.
I am trying to print a label on a zebra labelwriter with a logo (image in pcx file format).

According to epl2 programming guide i must send imange as "Raw binary data without graphic file formatting. Data must be in bytes"

How do i achive this ? (10.2B running on UNIX). How can i get that image as "raw binary data" ?

This is my code today. On my label, the logo is just "dark garbage
Code:
  def var memFile as memptr no-undo.
  def var cLogoBin as longchar no-undo.
  copy-lob from file "/opt/sesam/main/prog/img/kccare.pcx" to memFile no-convert no-error.
  cLogoBin = base64-encode(memFile).
 
  if avail routing then
  do:
    {gen/rtopen2.i}.
    display "N" WITH NO-LABELS. /* Tømmer minne */
    set x[01] = 'N'
        x[02] = 'R10,10'
        x[03] = 'S2'
        x[04] = 'D7'
        x[05] = 'ZT'
        x[06] = 'I8,1,001'
        x[07] = 'GW20,10,50,80,' + cLogoBin
        x[08] = 'A20,100'  + cLargeText + quoter("AssetNo:")
        x[09] = 'A240,100' + cLargeText + quoter("W339258")
        x[10] = 'B20,140,0,1,4,2,60,N,' + quoter("W339258")  /* barcode */
        x[11] = 'P1'. 
    do i = 1 to 11:
      put unformatted x[i] skip.
    end.
    {gen/rtclose2.i} .
  end.
 

Cringer

ProgressTalk.com Moderator
Staff member
Wouldn't it be easier to create a .zpl in label designer and drop that onto the printer?
 

Cecil

19+ years progress programming and still learning.
Just having a quick look at Wikipedia and the file structure of a PCX image. The first 128 bytes is the image header. Going out on a wilde guess, what would happen if you stripped off the first 128 bytes of the image file before copying to the memptr?

Code:
copy-lob from file "/opt/sesam/main/prog/img/kccare.pcx" starting at 128 to memFile no-convert no-error.

or

Code:
copy-lob from file "/opt/sesam/main/prog/img/kccare.pcx" starting at 128 + 1 to memFile no-convert no-error.
 
Last edited:

Cecil

19+ years progress programming and still learning.
Also, looking at the documentation for the EPL2 programing language it does not say anything about base64 encoding the binary image. Can you add some clarity to this?

Since the printer stream now a mix of both ASCII characters & Raw binary, you might want to rethink how you are building up the EPL instruction set into a memptr rather than a character array/extent.

I had an hour of spare time this morning. Here is some non-tested code to help you along. I don't have a Zebra printer (have not touched one in 15+ year) so I can't really test. The code is all theoretical. Let the Progress Talk community on how you got on.


Code:
function addMemptrToMemptr return memptr (input sourceMemptr    as memptr,
                                          input appendingMemptr as memptr):
                                              
    define variable targetMemptr            as Memptr no-undo.
    define variable sourceMemptrLength      as integer no-undo.
    define variable appendingMemptrLength   as integer no-undo.
  
    sourceMemptrLength    = get-size(sourceMemptr).
    appendingMemptrLength = get-size(appendingMemptr).
  
    set-size(targetMemptr) = 0. /** Allway do this **/
  
    if sourceMemptrLength eq 0 and appendingMemptrLength eq 0 then
        return targetMemptr.
  
    /** set the size of the memptr into now include
        the length of the appending string. **/
    set-size(targetMemptr) = sourceMemptrLength + appendingMemptrLength   .
  
    /** Overlay the soure memptr over to the target memptr**/
    copy-lob from object sourceMemptr starting at 1 for sourceMemptrLength to object targetMemptr overlay at 1.
  
    /** Overlay the appending memptr over to the target memptr**/
    copy-lob from object appendingMemptr starting at 1 for appendingMemptrLength to object targetMemptr overlay at sourceMemptrLength + 1.
  
    /** Tidy up.. **/
    set-size(sourceMemptr)    = 0. /** Allway do this **/
    set-size(appendingMemptr) = 0. /** Allway do this **/
                                            
    return targetMemptr.

end function.

function appendStringToMemptr return memptr (input sourceMemptr as memptr,
                                             input appendingString as character).
                                              
    define variable targetMemptr            as Memptr no-undo.
    define variable sourceMemptrLength      as integer no-undo.
    define variable appendingStringLength   as integer no-undo.
  
    sourceMemptrLength    = get-size(sourceMemptr).
    appendingStringLength = length(appendingString, "RAW" ).
  
    set-size(targetMemptr) = 0. /** Allway do this **/
  
    /** safty check..**/
    if sourceMemptrLength eq 0 and appendingStringLength eq 0 then 
        return targetMemptr.
      
    /** set the size of the memptr into now include
        the length of the appending string. **/
    set-size(targetMemptr) = sourceMemptrLength + appendingStringLength .
  
    /** Overlay the soure memptr over to the target memptr**/
    copy-lob from object sourceMemptr starting at 1 for sourceMemptrLength to object targetMemptr overlay at 1.
  
    /** Append the string to the newly created memptr.**/
    put-string(targetMemptr,sourceMemptrLength + 1, appendingStringLength ) = appendingString.
  
    /** Tidy up.. **/
    set-size(sourceMemptr ) = 0. /** Allway do this **/
                                            
    return targetMemptr.
end function.


define variable EPLControlCodes as Memptr no-undo.
define variable PCXImage        as Memptr no-undo.

define variable cLargeText as character no-undo.

/** **/
copy-lob from file "/opt/sesam/main/prog/img/kccare.pcx" starting at 128 to PCXImage.

/** or **/
/** copy-lob from file "/opt/sesam/main/prog/img/kccare.pcx" starting at 129 to PCXImage. **/

EPLControlCodes = appendStringToMemptr(EPLControlCodes , "N~n").
EPLControlCodes = appendStringToMemptr(EPLControlCodes , "R10,10~n" ).
EPLControlCodes = appendStringToMemptr(EPLControlCodes , "S2~n").
EPLControlCodes = appendStringToMemptr(EPLControlCodes , "D7~n").
EPLControlCodes = appendStringToMemptr(EPLControlCodes , "ZT~n").
EPLControlCodes = appendStringToMemptr(EPLControlCodes , "I8,1,001~n").
EPLControlCodes = appendStringToMemptr(EPLControlCodes , "GW20,10,50,80,"). /** Note. No termination added.**/

EPLControlCodes = addMemptrToMemptr(EPLControlCodes , PCXImage).    /** Append the binary image into the memptr stream...**/
EPLControlCodes = appendStringToMemptr(EPLControlCodes , "~n").   /** teminate GW command with new line ~n = chr(10) **/


EPLControlCodes = appendStringToMemptr(EPLControlCodes , "A20,100" + cLargeText + quoter("AssetNo:") + "~n").
EPLControlCodes = appendStringToMemptr(EPLControlCodes , "A240,100" + cLargeText + quoter("AssetNo:") + "~n").
EPLControlCodes = appendStringToMemptr(EPLControlCodes , "B20,140,0,1,4,2,60,N," + quoter("AssetNo:") + "~n").
EPLControlCodes = appendStringToMemptr(EPLControlCodes , "P1~n").



  {gen/rtopen2.i}.
  export EPLControlCodes.
     {gen/rtclose2.i} .
  
    /** Tidy up memory..**/
    set-size(PCXImage) = 0. 

/** debugging  **/
copy-lob from object EPLControlCodes to file "./epl2debug.txt".






Referenace:
https://www.zebra.com/content/dam/zebra/manuals/en-us/printer/epl2-pm-en.pdf page 110
 
Last edited:

Cecil

19+ years progress programming and still learning.
Also, I noticed in the documentation for epl2 the colour count for the PCX must be 2 (black & white).

Additionally, if the image a common repetitive the logo, the image can be stored in the printer's memory. See GK and GM command. Looking at the documentation, it looks like it does not need to have the file format header removed. It should be simpler to recall the image to be outputted.

Of course, if the image is being recalled from a collection of image files from disk, this might not be the best solution.
 

Cecil

19+ years progress programming and still learning.
Okay.. I've been intrigued by this problem. The main issue is that the image (not necessary PCX) need to be decoded from it native file format into a raw uncompress binary. The confusion for me was PCX is a binary file but it has a run-length compression which needs to be uncompressed for the zebra printer.

Each byte has 8 bits, of course, and each bit represents one graphic dot. In the binary data, a 1 = white dot and 0 = black dot.

I found some C code to uncompress PCX files but it not clear what it's doing:

Code:
Sample "C" Routines

The following is a simple set of C subroutines to read data from a PCX file.

/* This procedure reads one encoded block from the image file and
stores a count and data byte. Result:
  0 = valid data stored
  EOF = out of data in file */

encget(pbyt, pcnt, fid)
int *pbyt;     /* where to place data */
int *pcnt;     /* where to place count */
FILE *fid;     /* image file handle */
{
int i;
    *pcnt = 1;     /* safety play */
    if(EOF    ==    (i    =    getc(fid))) return(EOF);
    if(0xc0 == (0xc0 & i))   {
     *pcnt = 0x3f&i;
    if(EOF == (i=getc(fid)))
            return(EOF);
}
*pbyt = i;
return(0);
}
 
Hi all

I have a problem that mr. Google can't help me with.
I am trying to print a label on a zebra labelwriter with a logo (image in pcx file format).

According to epl2 programming guide i must send imange as "Raw binary data without graphic file formatting. Data must be in bytes"

How do i achive this ? (10.2B running on UNIX). How can i get that image as "raw binary data" ?

This is my code today. On my label, the logo is just "dark garbage
Code:
  def var memFile as memptr no-undo.
  def var cLogoBin as longchar no-undo.
  copy-lob from file "/opt/sesam/main/prog/img/kccare.pcx" to memFile no-convert no-error.
  cLogoBin = base64-encode(memFile).
 
  if avail routing then
  do:
    {gen/rtopen2.i}.
    display "N" WITH NO-LABELS. /* Tømmer minne */
    set x[01] = 'N'
        x[02] = 'R10,10'
        x[03] = 'S2'
        x[04] = 'D7'
        x[05] = 'ZT'
        x[06] = 'I8,1,001'
        x[07] = 'GW20,10,50,80,' + cLogoBin
        x[08] = 'A20,100'  + cLargeText + quoter("AssetNo:")
        x[09] = 'A240,100' + cLargeText + quoter("W339258")
        x[10] = 'B20,140,0,1,4,2,60,N,' + quoter("W339258")  /* barcode */
        x[11] = 'P1'.
    do i = 1 to 11:
      put unformatted x[i] skip.
    end.
    {gen/rtclose2.i} .
  end.

Here an useful site Labelary Online ZPL Viewer
You can paste your ZPL code and see the label on the screen.
What is the best part? You can upload an image and the app will show the ZPL code.
 

Cecil

19+ years progress programming and still learning.
Here is my first attempt to decode a PCX image file. No way is it perfect and it needs some major cleanup. There is a lot of sample code (non-ABL) on the net which handles the decoding of PCX files. It needs better error handling and it also needs to detect when it has gotten to the end of image (last pixel).

Code:
define variable pcxheader as memptr no-undo.
define variable pcxImage  as memptr no-undo.

SET-BYTE-ORDER( pcxheader ) = LITTLE-ENDIAN.
SET-BYTE-ORDER( pcxImage  ) = LITTLE-ENDIAN.

define variable bitsPerPixel as integer no-undo.

define variable xMin as integer.
define variable yMin as integer.
define variable xMax as integer.
define variable yMax as integer.

define variable PCXWidth  as integer.
define variable PCXHeight as integer.

define variable NPlanes      as integer.
define variable bytesPerLine as integer.
define variable bytesPerScanline   as integer.

define variable byte           as integer no-undo. 
define variable runValue       as integer no-undo. 
define variable offSet         as integer no-undo. 
define variable runLength      as integer no-undo.
define variable chunkPosition  as integer no-undo.
define variable i              as integer no-undo.
define variable runLengthDecode as integer no-undo.

copy-lob from file "C:\Users\James\Pictures\Untitled.pcx" for 128 TO object pcxHeader.
copy-lob from file "C:\Users\James\Pictures\Untitled.pcx" starting at 128 + 1 TO object pcxImage.


    bitsPerPixel = GET-UNSIGNED-SHORT(pcxheader, 3 + 1).



    xMin = GET-UNSIGNED-SHORT(pcxheader, 5).
    yMin = GET-UNSIGNED-SHORT(pcxheader, 7).
    xMax = GET-UNSIGNED-SHORT(pcxheader, 9).
    yMax = GET-UNSIGNED-SHORT(pcxheader, 11).


    NPlanes      = GET-BYTE(pcxheader, 65 + 1).
    bytesPerLine = GET-BYTE(pcxheader, 66 + 1).
  
    PCXWidth  = xMax - xMin + 1.
    PCXHeight = yMax - yMin + 1. 
  
    bytesPerScanline   = NPlanes * bytesPerLine.
  
message xMax - xMin + 1 skip
        yMax - yMin + 1 skip(1)
        NPlanes      skip
        bytesPerLine skip(1)
        bytesPerScanline 
        view-as alert-box info. 
  
  
 
      
define stream sOutput.

output stream sOutput to "pcxdecoded.txt" NO-CONVERT.
      
PARSEPCX:     
DO chunkPosition = 1 TO get-size(pcxImage):

    /** Read the file in chunks equal to the
        length of the bytesPerScanline **/ 
      
    byte = GET-BYTE(pcxImage, chunkPosition ).
  
    if byte ge 0xC0 then
        assign
            runLength       = get-bits(byte ,1,6)
            runValue        = GET-BYTE(pcxImage, chunkPosition + 1)
            chunkPosition   = chunkPosition + 1.
    else
        assign
            runLength = 1
            runValue  = byte.
  
    do i = 1 to runLength:
        put stream sOutput unformatted runValue.
    end.
  
    pause 0.
end. 

output stream sOutput close.

Screen Shot 09-29-17 at 03.12 PM.PNG Screen Shot 09-29-17 at 03.12 PM 001.PNG
 

bigwill

Member
Here an useful site Labelary Online ZPL Viewer
You can paste your ZPL code and see the label on the screen.
What is the best part? You can upload an image and the app will show the ZPL code.
Thank you for this tip. Unfortunately my label writer is old and only supports epl code. I have spent too many hours trying to get this to work with epl, much cheaper to buy a new labelwriter that supports zpl and use that for the future
 

bigwill

Member
Here is my first attempt to decode a PCX image file. No way is it perfect and it needs some major cleanup. There is a lot of sample code (non-ABL) on the net which handles the decoding of PCX files. It needs better error handling and it also needs to detect when it has gotten to the end of image (last pixel).

Code:
define variable pcxheader as memptr no-undo.
define variable pcxImage  as memptr no-undo.

SET-BYTE-ORDER( pcxheader ) = LITTLE-ENDIAN.
SET-BYTE-ORDER( pcxImage  ) = LITTLE-ENDIAN.

define variable bitsPerPixel as integer no-undo.

define variable xMin as integer.
define variable yMin as integer.
define variable xMax as integer.
define variable yMax as integer.

define variable PCXWidth  as integer.
define variable PCXHeight as integer.

define variable NPlanes      as integer.
define variable bytesPerLine as integer.
define variable bytesPerScanline   as integer.

define variable byte           as integer no-undo.
define variable runValue       as integer no-undo.
define variable offSet         as integer no-undo.
define variable runLength      as integer no-undo.
define variable chunkPosition  as integer no-undo.
define variable i              as integer no-undo.
define variable runLengthDecode as integer no-undo.

copy-lob from file "C:\Users\James\Pictures\Untitled.pcx" for 128 TO object pcxHeader.
copy-lob from file "C:\Users\James\Pictures\Untitled.pcx" starting at 128 + 1 TO object pcxImage.


    bitsPerPixel = GET-UNSIGNED-SHORT(pcxheader, 3 + 1).



    xMin = GET-UNSIGNED-SHORT(pcxheader, 5).
    yMin = GET-UNSIGNED-SHORT(pcxheader, 7).
    xMax = GET-UNSIGNED-SHORT(pcxheader, 9).
    yMax = GET-UNSIGNED-SHORT(pcxheader, 11).


    NPlanes      = GET-BYTE(pcxheader, 65 + 1).
    bytesPerLine = GET-BYTE(pcxheader, 66 + 1).
 
    PCXWidth  = xMax - xMin + 1.
    PCXHeight = yMax - yMin + 1.
 
    bytesPerScanline   = NPlanes * bytesPerLine.
 
message xMax - xMin + 1 skip
        yMax - yMin + 1 skip(1)
        NPlanes      skip
        bytesPerLine skip(1)
        bytesPerScanline
        view-as alert-box info.
 
 
 
     
define stream sOutput.

output stream sOutput to "pcxdecoded.txt" NO-CONVERT.
     
PARSEPCX:    
DO chunkPosition = 1 TO get-size(pcxImage):

    /** Read the file in chunks equal to the
        length of the bytesPerScanline **/
     
    byte = GET-BYTE(pcxImage, chunkPosition ).
 
    if byte ge 0xC0 then
        assign
            runLength       = get-bits(byte ,1,6)
            runValue        = GET-BYTE(pcxImage, chunkPosition + 1)
            chunkPosition   = chunkPosition + 1.
    else
        assign
            runLength = 1
            runValue  = byte.
 
    do i = 1 to runLength:
        put stream sOutput unformatted runValue.
    end.
 
    pause 0.
end.

output stream sOutput close.

View attachment 1566 View attachment 1565


Wow. Thank you very much for trying to get this to work. I have used days now to try different things. By using zebra desig program i can get it to work, but i have no luck when i try to read that pcx file from a procedure. The size/length when i try to read the pcx file is so much larger than what the zebra design program builds up, so i don't know. There is a mismatch in bytes and parameter 3 and 4 when printing graphics using epl. If only i could get these values, then i think it could work. But these epl printers are old, and the new programming language for zebra printers is zpl. So i thing the cheapes solution is to buy new label printers and write labels using zpl codes instread. But again, thank you for trying :)
 

Cecil

19+ years progress programming and still learning.
Food for thought. In the past, I've used the PDFinclude function to create a PDF document which can handle barcodes and images. Using Linux CUPS printing I was able to print the PDF to a Dymo Label printer.

I have not tried it myself but I believe there is PDF to ZPL/EPL2 printer drivers/converters. I not sure how robust these converts are but it might be an alternative.
 

Cringer

ProgressTalk.com Moderator
Staff member
ZPL prints barcodes natively so printing to PDF and converting is a redundant step IMO.
 

Cecil

19+ years progress programming and still learning.
ZPL prints barcodes natively so printing to PDF and converting is a redundant step IMO.

The point I was sort of trying to make is that PDF documents are a good all round base solution when needing to create a printable document on both Linux and Windows. Yes, it is an extra setup creating a PDF file into other formats but it allows the developer to have that flexibility.

Personally, I love using the wkhtmltopdf converter, I have been able to create some highly professional looking reports using this method of creating HTML to PDF and then converting PDF to whatever.

Also, ZPL only works on Zebra Printers. So from an owners point-of-view, you are committed to buying Zebra Printer just because the application requires it's.

On the other hand, what would be very handy is an open-source class rapper library which encapsulated the ZPL (or even EPL) commands written for the ABL.

......


Continuing on the EPL path, Zebra provides a JAVA API which can convert graphic images into a RAW bitmap format (.GRF). In theory might be possible from the command line to convert an image into the required needed format.

Code:
java -jar ZSDK_API.jar graphic myImage.jpeg -s myConvertedImage.GRF
 
Top