1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Question Read File (img) Binary

Discussion in 'Development' started by bigwill, Sep 27, 2017.

  1. bigwill

    bigwill New 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 (progress):
    1.  
    2.   def var memFile as memptr no-undo.
    3.   def var cLogoBin as longchar no-undo.
    4.   copy-lob from file "/opt/sesam/main/prog/img/kccare.pcx" to memFile no-convert no-error.
    5.   cLogoBin = base64-encode(memFile).
    6.  
    7.   if avail routing then
    8.   do:
    9.     {gen/rtopen2.i}.
    10.     display "N" WITH NO-LABELS. /* Tømmer minne */
    11.     set x[01] = 'N'
    12.         x[02] = 'R10,10'
    13.         x[03] = 'S2'
    14.         x[04] = 'D7'
    15.         x[05] = 'ZT'
    16.         x[06] = 'I8,1,001'
    17.         x[07] = 'GW20,10,50,80,' + cLogoBin
    18.         x[08] = 'A20,100'  + cLargeText + quoter("AssetNo:")
    19.         x[09] = 'A240,100' + cLargeText + quoter("W339258")
    20.         x[10] = 'B20,140,0,1,4,2,60,N,' + quoter("W339258")  /* barcode */
    21.         x[11] = 'P1'.
    22.     do i = 1 to 11:
    23.       put unformatted x[i] skip.
    24.     end.
    25.     {gen/rtclose2.i} .
    26.   end.
     
  2.  
  3. Cringer

    Cringer ProgressTalk.com Moderator Staff Member

    Wouldn't it be easier to create a .zpl in label designer and drop that onto the printer?
     
  4. Cecil

    Cecil 17+ 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 (progress):
    1. copy-lob from file "/opt/sesam/main/prog/img/kccare.pcx" starting at 128 to memFile no-convert no-error.
    or

    Code (progress):
    1. copy-lob from file "/opt/sesam/main/prog/img/kccare.pcx" starting at 128 + 1 to memFile no-convert no-error.
     
    Last edited: Sep 27, 2017
  5. Cecil

    Cecil 17+ 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 (progress):
    1. function addMemptrToMemptr return memptr (input sourceMemptr    as memptr,
    2.                                           input appendingMemptr as memptr):
    3.                                              
    4.     define variable targetMemptr            as Memptr no-undo.
    5.     define variable sourceMemptrLength      as integer no-undo.
    6.     define variable appendingMemptrLength   as integer no-undo.
    7.  
    8.     sourceMemptrLength    = get-size(sourceMemptr).
    9.     appendingMemptrLength = get-size(appendingMemptr).
    10.  
    11.     set-size(targetMemptr) = 0. /** Allway do this **/
    12.  
    13.     if sourceMemptrLength eq 0 and appendingMemptrLength eq 0 then
    14.         return targetMemptr.
    15.  
    16.     /** set the size of the memptr into now include
    17.         the length of the appending string. **/
    18.     set-size(targetMemptr) = sourceMemptrLength + appendingMemptrLength   .
    19.  
    20.     /** Overlay the soure memptr over to the target memptr**/
    21.     copy-lob from object sourceMemptr starting at 1 for sourceMemptrLength to object targetMemptr overlay at 1.
    22.  
    23.     /** Overlay the appending memptr over to the target memptr**/
    24.     copy-lob from object appendingMemptr starting at 1 for appendingMemptrLength to object targetMemptr overlay at sourceMemptrLength + 1.
    25.  
    26.     /** Tidy up.. **/
    27.     set-size(sourceMemptr)    = 0. /** Allway do this **/
    28.     set-size(appendingMemptr) = 0. /** Allway do this **/
    29.                                            
    30.     return targetMemptr.
    31.  
    32. end function.
    33.  
    34. function appendStringToMemptr return memptr (input sourceMemptr as memptr,
    35.                                              input appendingString as character).
    36.                                              
    37.     define variable targetMemptr            as Memptr no-undo.
    38.     define variable sourceMemptrLength      as integer no-undo.
    39.     define variable appendingStringLength   as integer no-undo.
    40.  
    41.     sourceMemptrLength    = get-size(sourceMemptr).
    42.     appendingStringLength = length(appendingString, "RAW" ).
    43.  
    44.     set-size(targetMemptr) = 0. /** Allway do this **/
    45.  
    46.     /** safty check..**/
    47.     if sourceMemptrLength eq 0 and appendingStringLength eq 0 then
    48.         return targetMemptr.
    49.      
    50.     /** set the size of the memptr into now include
    51.         the length of the appending string. **/
    52.     set-size(targetMemptr) = sourceMemptrLength + appendingStringLength .
    53.  
    54.     /** Overlay the soure memptr over to the target memptr**/
    55.     copy-lob from object sourceMemptr starting at 1 for sourceMemptrLength to object targetMemptr overlay at 1.
    56.  
    57.     /** Append the string to the newly created memptr.**/
    58.     put-string(targetMemptr,sourceMemptrLength + 1, appendingStringLength ) = appendingString.
    59.  
    60.     /** Tidy up.. **/
    61.     set-size(sourceMemptr ) = 0. /** Allway do this **/
    62.                                            
    63.     return targetMemptr.
    64. end function.
    65.  
    66.  
    67. define variable EPLControlCodes as Memptr no-undo.
    68. define variable PCXImage        as Memptr no-undo.
    69.  
    70. define variable cLargeText as character no-undo.
    71.  
    72. /** **/
    73. copy-lob from file "/opt/sesam/main/prog/img/kccare.pcx" starting at 128 to PCXImage.
    74.  
    75. /** or **/
    76. /** copy-lob from file "/opt/sesam/main/prog/img/kccare.pcx" starting at 129 to PCXImage. **/
    77.  
    78. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "N~n").
    79. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "R10,10~n" ).
    80. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "S2~n").
    81. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "D7~n").
    82. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "ZT~n").
    83. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "I8,1,001~n").
    84. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "GW20,10,50,80,"). /** Note. No termination added.**/
    85.  
    86. EPLControlCodes = addMemptrToMemptr(EPLControlCodes , PCXImage).    /** Append the binary image into the memptr stream...**/
    87. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "~n").   /** teminate GW command with new line ~n = chr(10) **/
    88.  
    89.  
    90. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "A20,100" + cLargeText + quoter("AssetNo:") + "~n").
    91. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "A240,100" + cLargeText + quoter("AssetNo:") + "~n").
    92. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "B20,140,0,1,4,2,60,N," + quoter("AssetNo:") + "~n").
    93. EPLControlCodes = appendStringToMemptr(EPLControlCodes , "P1~n").
    94.  
    95.  
    96.  
    97.   {gen/rtopen2.i}.
    98.   export EPLControlCodes.
    99.      {gen/rtclose2.i} .
    100.  
    101.     /** Tidy up memory..**/
    102.     set-size(PCXImage) = 0.
    103.  
    104. /** debugging  **/
    105. copy-lob from object EPLControlCodes to file "./epl2debug.txt".
    106.  
    107.  





    Referenace:
    https://www.zebra.com/content/dam/zebra/manuals/en-us/printer/epl2-pm-en.pdf page 110
     
    Last edited: Sep 27, 2017
    Marco Mendoza likes this.
  6. Cecil

    Cecil 17+ 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.
     
  7. Cecil

    Cecil 17+ 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 (progress):
    1. Sample "C" Routines
    2.  
    3. The following is a simple set of C subroutines to read data from a PCX file.
    4.  
    5. /* This procedure reads one encoded block from the image file and
    6. stores a count and data byte. Result:
    7.   0 = valid data stored
    8.   EOF = out of data in file */
    9.  
    10. encget(pbyt, pcnt, fid)
    11. int *pbyt;     /* where to place data */
    12. int *pcnt;     /* where to place count */
    13. FILE *fid;     /* image file handle */
    14. {
    15. int i;
    16.     *pcnt = 1;     /* safety play */
    17.     if(EOF    ==    (i    =    getc(fid))) return(EOF);
    18.     if(0xc0 == (0xc0 & i))   {
    19.      *pcnt = 0x3f&i;
    20.     if(EOF == (i=getc(fid)))
    21.             return(EOF);
    22. }
    23. *pbyt = i;
    24. return(0);
    25. }
     
  8. Marco Mendoza

    Marco Mendoza 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.
     
  9. Cecil

    Cecil 17+ 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 (progress):
    1. define variable pcxheader as memptr no-undo.
    2. define variable pcxImage  as memptr no-undo.
    3.  
    4. SET-BYTE-ORDER( pcxheader ) = LITTLE-ENDIAN.
    5. SET-BYTE-ORDER( pcxImage  ) = LITTLE-ENDIAN.
    6.  
    7. define variable bitsPerPixel as integer no-undo.
    8.  
    9. define variable xMin as integer.
    10. define variable yMin as integer.
    11. define variable xMax as integer.
    12. define variable yMax as integer.
    13.  
    14. define variable PCXWidth  as integer.
    15. define variable PCXHeight as integer.
    16.  
    17. define variable NPlanes      as integer.
    18. define variable bytesPerLine as integer.
    19. define variable bytesPerScanline   as integer.
    20.  
    21. define variable byte           as integer no-undo.
    22. define variable runValue       as integer no-undo.
    23. define variable offSet         as integer no-undo.
    24. define variable runLength      as integer no-undo.
    25. define variable chunkPosition  as integer no-undo.
    26. define variable i              as integer no-undo.
    27. define variable runLengthDecode as integer no-undo.
    28.  
    29. copy-lob from file "C:\Users\James\Pictures\Untitled.pcx" for 128 TO object pcxHeader.
    30. copy-lob from file "C:\Users\James\Pictures\Untitled.pcx" starting at 128 + 1 TO object pcxImage.
    31.  
    32.  
    33.     bitsPerPixel = GET-UNSIGNED-SHORT(pcxheader, 3 + 1).
    34.  
    35.  
    36.  
    37.     xMin = GET-UNSIGNED-SHORT(pcxheader, 5).
    38.     yMin = GET-UNSIGNED-SHORT(pcxheader, 7).
    39.     xMax = GET-UNSIGNED-SHORT(pcxheader, 9).
    40.     yMax = GET-UNSIGNED-SHORT(pcxheader, 11).
    41.  
    42.  
    43.     NPlanes      = GET-BYTE(pcxheader, 65 + 1).
    44.     bytesPerLine = GET-BYTE(pcxheader, 66 + 1).
    45.  
    46.     PCXWidth  = xMax - xMin + 1.
    47.     PCXHeight = yMax - yMin + 1.
    48.  
    49.     bytesPerScanline   = NPlanes * bytesPerLine.
    50.  
    51. message xMax - xMin + 1 skip
    52.         yMax - yMin + 1 skip(1)
    53.         NPlanes      skip
    54.         bytesPerLine skip(1)
    55.         bytesPerScanline
    56.         view-as alert-box info.
    57.  
    58.  
    59.  
    60.      
    61. define stream sOutput.
    62.  
    63. output stream sOutput to "pcxdecoded.txt" NO-CONVERT.
    64.      
    65. PARSEPCX:    
    66. DO chunkPosition = 1 TO get-size(pcxImage):
    67.  
    68.     /** Read the file in chunks equal to the
    69.         length of the bytesPerScanline **/
    70.      
    71.     byte = GET-BYTE(pcxImage, chunkPosition ).
    72.  
    73.     if byte ge 0xC0 then
    74.         assign
    75.             runLength       = get-bits(byte ,1,6)
    76.             runValue        = GET-BYTE(pcxImage, chunkPosition + 1)
    77.             chunkPosition   = chunkPosition + 1.
    78.     else
    79.         assign
    80.             runLength = 1
    81.             runValue  = byte.
    82.  
    83.     do i = 1 to runLength:
    84.         put stream sOutput unformatted runValue.
    85.     end.
    86.  
    87.     pause 0.
    88. end.
    89.  
    90. output stream sOutput close.    
    91.  
    92.  
    Screen Shot 09-29-17 at 03.12 PM.PNG Screen Shot 09-29-17 at 03.12 PM 001.PNG
     
  10. bigwill

    bigwill New Member

    Not possible in my state. I must build up text barcodes based on input values.
     
  11. bigwill

    bigwill New Member

    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
     
  12. bigwill

    bigwill New Member


    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 :)
     
  13. Cecil

    Cecil 17+ 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.
     
  14. Cringer

    Cringer ProgressTalk.com Moderator Staff Member

    ZPL prints barcodes natively so printing to PDF and converting is a redundant step IMO.
     
  15. Cecil

    Cecil 17+ years progress programming and still learning.

    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 (progress):
    1. java -jar ZSDK_API.jar graphic myImage.jpeg -s myConvertedImage.GRF
     

Share This Page