[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Expat & Base64




Everything seems to be ok however when I open the document off the iSeries
Adobe can not read it...

How are you retrieving it from the iSeries? Hopefully using something that won't try to translate it in any way? PDF documents are considered "binary" documents, and you need to be careful to make sure that absolutely no translation takes place after it's been decoded.


I have added code to create a stream file on the IFS for the Start of the Elem and Close it at the End. For the CharData I have added code to call decode with the data and place it in the file on the IFS....

No offense, but I really don't have the time to figure out what your code does. (You might notice that I'm writing this e-mail at 4am!) I made your code work on my machine, here's what I did:


a) Before I could do anything, I had to strip the line numbers and dates from your RPG code. I asked you previously to remove these, but you simply re-posted the code without removing them. Frustrating. So, I wrote a Unix shell script to strip them off.

b) You check fd2 for *zeros all over your code, and when it's zero, you don't write data to the file. Why would you do that? Zero is a valid file descriptor! If open() returns zero, it means that it was successful and the file is open! I changed it to use -1 instead of zero. -1 is the value open() returns when it fails, so that also means that writing to the file fails if the file doesn't open successfully -- which is probably what you want (Though, I'd suggest setting an error message and showing it to someone so that people aren't simply baffled.)

c) There are blanks in the base64 data in the file. Since blanks aren't legal characters in the base64 alphabet, they need to be stripped from the data before you decode it. If not, you'll end up corrupting the result.

d) I didn't want to take the time to figure out your logic for making sure the data was an even multiple of 4 bytes long. But, it seems to me that there was a chance that data would still be in the "leftovr" field when the end element arrived, and this could cause part of the data to never be written. (I could be wrong, since I didn't spend a lot of time on it.)


My solution was to create a temporary/unlinked file in the IFS to put the base64 encoded data into. Whenever chardata() is called, I scan through "val" looking for blanks. Anything that's not a blank gets written to the temp file.


When I reach the end element handler at the end of the data for the file, I seek back to the start of the temp file and read through it in chunks that are even multiples of 4 bytes. (I used 32k chunks for performance reasons, but it should work with any even multiple of 4.)

I run each chunk of data through the base64 decoder, and write it out to /tmp/test.pdf. After I've done the entire file, I close both the temporary file and the /tmp/test.pdf file.

(Since the temporary file was already unlinked, closing it automatically deletes it.)

I FTPed the test.pdf file to my Windows box (using FTP in BINARY mode) and opened it, and it worked for me. I've attached my modified copy of the source to this message so that you can try it on your box.

Good luck
      * Demonstration of accumulating character data until the ending
      * XML element is found, then printing it.  This also demonstrates
      * printing XML attributes.
      *
      *                             Scott Klement, April 14, 2005
      *
      * To compile:
      *   CRTBNDRPG FULLELEM SRCFILE(xxx/QRPGLESRC) DBGVIEW(*LIST)
      *
      *
     H DFTACTGRP(*NO) ACTGRP(*NEW) BNDDIR('QC2LE':'EXPAT':'BASE64')

     FQSYSPRT   O    F  132        PRINTER

      /copy ifsio_h
      /copy errno_h
      /copy expat_h
      /copy iconv_h
      /copy PIALIB/SRVSRC,BASE64_H

     D UTF8            c                   x'5554462d38'

     D start           PR
     D   d                                 likeds(stack)
     D   elem                          *   value
     D   attr                          *   dim(32767) options(*varsize)
     D end             PR
     D   d                                 likeds(stack)
     D   elem                          *   value
     D chardata        PR
     D   d                                 likeds(stack)
     D   string                   65535A   options(*varsize)
     D   len                         10I 0 value
     D DecodeTempFile  PR

     D tmpnam          PR              *   extproc('_C_IFS_tmpnam')
     D   string                      39A   options(*omit)
     D tempfile        s             40A   varying

     D Buff            s            256A
     D PrintMe         s            132A   varying
     D ic              ds                  likeds(iconv_t)

     D stack           ds
     D   depth                       10I 0 inz
     D   elemName                  1024A   varying dim(30)
     D   elemValue                 1024A   varying dim(30)

     D from            ds                  likeds(iconv_code)
     D                                     inz(*LIKEDS)
     D to              ds                  likeds(iconv_code)
     D                                     inz(*LIKEDS)

     D fd              s             10I 0
     D p               s                   like(XML_Parser)
     D len             s             10I 0
     D done            s             10I 0
     D x               s             10I 0

     D Fd2             s             10I 0
     D leftovr         s          65535A   varying
     D leftl           s             10I 0

      /free

         //
         //  Set up the iconv() API for translating from
         //  UTF-8 to the current job's CCSID
         //

         from.CCSID = 1208;
         to.CCSID   = 0;
         fd2 = -1;
         ic = QtqIConvOpen(to: from);


         //
         //  Open XML document to parse.
         //  Ask the open() API to translate it into UTF-8 so
         //  that Expat will understand it.
         //

         fd = open( '/tmp/document.xml'
                  : O_RDONLY+O_TEXTDATA+O_CCSID
                  : 0
                  : 1208 );
         if (fd < 0);
           EscErrno(errno);
         endif;

         //
         // Create a "parser object" in memory that
         // will be used to parse the document.
         //

         p = XML_ParserCreate(UTF8);
         if (p = *NULL);
           callp close(fd);
           die('Couldn''t allocate memory for parser');
         endif;

         //
         // Register subprocedures to be called for the
         // starting and ending elements of an XML document
         //

         XML_SetStartElementHandler(p: %paddr(start));
         XML_SetEndElementHandler(p: %paddr(end));
         XML_SetCharacterDataHandler(p: %paddr(chardata));
         XML_SetUserData(p: %addr(stack));

         //
         // The following loop will read data from the XML
         // document, and feed it into the XML parser
         //
         // The parser will call the "start" and "end"
         // as the correct data is fed to it, and they'll
         // create the outline.
         //

         dou (done = 1);

             len = read(fd: %addr(Buff): %size(Buff));

             if (len < 1);
                done = 1;
             endif;

             if (XML_Parse(p: Buff: len: done) = XML_STATUS_ERROR);
                 callp close(fd);
                 die('Parse error at line '
                    + %char(XML_GetCurrentLineNumber(p)) + ': '
                    + %str(XML_ErrorString(XML_GetErrorCode(p))));
             endif;

         enddo;

         //
         // Done parsing... clean up!
         //

         XML_ParserFree(p);
         callp close(fd);
         iconv_close(ic);

         *inlr = *on;
      /end-free

     OQSYSPRT   E            Print
     O                       PrintMe            132


     P start           B
     D start           PI
     D   d                                 likeds(stack)
     D   elem                          *   value
     D   attr                          *   dim(32767) options(*varsize)

     D elemName        s             50A
     D attrName        s             50A
     D attrVal         s             50A
     D x               s             10I 0

     D p_input         s               *
     D inputlen        s             10U 0
     D p_output        s               *
     D outputlen       s             10U 0

      /free
         d.depth = d.depth + 1;

         p_input   = elem;
         inputlen  = %len(%str(elem));
         p_output  = %addr(elemName);
         outputlen = %size(elemName);
         iconv(ic: p_input: inputlen: p_output: outputlen);

         if (d.depth = 1);
            d.elemName(d.depth) = '/' + %trimr(elemName);
         else;
            d.elemName(d.depth) = d.elemName(d.depth-1)
                                + '/' + %trimr(elemName);
         endif;

        // Create a temporary file in the IFS to contain the data in
        // base64 format.  "Unlink" it so that it's invisible to other
        // programs on the system.

         if d.elemName(d.depth) =
             '/TRANSACTION/ATTACHMENTS/ATTACHMENT/FILE_BASE64_DATA';
           tempfile = %str(tmpnam(*omit));
           fd2 = open( tempfile
                     : O_CREAT + O_EXCL + O_RDWR
                     : M_RDWR );
           unlink( tempfile );
         endIf;

         d.elemValue(d.depth) = '';

         // -----------------------------------------
         // process XML element attributes
         // -----------------------------------------

         x = 1;
         dow attr(x) <> *NULL;

            // convert attribute name & value to EBCDIC

            AttrName = *blanks;
            p_input = attr(x);
            inputlen = %len(%str(attr(x)));
            p_output = %addr(AttrName);
            outputlen = %size(attrName);
            iconv(ic: p_input: inputlen: p_output: outputlen);

            AttrVal = *blanks;
            p_input = attr(x+1);
            inputlen = %len(%str(attr(x+1)));
            p_output = %addr(AttrVal);
            outputlen = %size(AttrVal);
            iconv(ic: p_input: inputlen: p_output: outputlen);

            // print attribute name & value

            PrintMe = %trimr(AttrName) + ' = ' + AttrVal;
            except Print;

            x = x + 2;
         enddo;

      /end-free
     P                 E


     P end             B
     D end             PI
     D   d                                 likeds(stack)
     D   elem                          *   value
      /free

          //
          //  Print element value
          //

          PrintMe = d.elemName(d.depth) + ' = '
                  + d.elemValue(d.depth);

          if d.elemName(d.depth) =
             '/TRANSACTION/ATTACHMENTS/ATTACHMENT/FILE_BASE64_DATA'
             and fd2 <> -1;
            DecodeTempFile();
            fd2 = -1;
          endIf;

          except Print;

          d.depth = d.depth - 1;
      /end-free
     P                 E


     P chardata        B
     D chardata        PI
     D   d                                 likeds(stack)
     D   string                   65535A   options(*varsize)
     D   len                         10I 0 value

     D val             s          65535A
     D val2            s          65535A   varying
     D p_input         s               *
     D inputlen        s             10U 0
     D p_output        s               *
     D outputlen       s             10U 0

     D b64outlen       s             10U 0
     D x               s             10I 0
      /free
         if (len < 1);
            return;
         endif;

         // convert data to EBCDIC

         val = *blanks;
         p_input = %addr(string);
         inputlen = len;
         p_output = %addr(val);
         outputlen = %size(val);
         iconv(ic: p_input: inputlen: p_output: outputlen);

         // add to end of element value.

         len = %size(val) - outputLen;
         %len(val2) = 0;

         if d.elemName(d.depth) =
            '/TRANSACTION/ATTACHMENTS/ATTACHMENT/FILE_BASE64_DATA'
            and fd2 <> -1;

            // Remove spaces from data.

            for x = 1 to len;
              if %subst(val:x:1) <> ' ';
                 val2 = val2 + %subst(val:x:1);
              endif;
            endfor;

           // Write base64 encoded data to temp file

           callp write(fd2:%Addr(val2)+2: %len(val2));
         endIf;

      /end-free
     P                 E

     P DecodeTempFile  B
     D DecodeTempFile  PI

     D fd3             s             10I 0
     D len             s             10I 0
     D buf             s          32768A
     D outbuf          s          24576A
     D outlen          s             10I 0
      /free

        fd3 = open('/tmp/test.pdf'
                  : O_WRONLY + O_CREAT + O_TRUNC + O_CCSID
                  : M_RDWR
                  : 819 );

        if (fd3 <> -1);

           lseek(fd2: 0: SEEK_SET);
           len = read(fd2: %addr(buf): %size(buf));

           dow len > 0;
              outlen = base64_decode( %addr(buf)
                                    : len
                                    : %addr(outbuf)
                                    : %size(outbuf) );
              callp write(fd3: %addr(outbuf): outlen);
              len = read(fd2: %addr(buf): %size(buf));
           enddo;

           callp close(fd3);

        endif;

        callp close(fd2);
        fd2 = -1;

      /end-free
     P                 E

      /define ERRNO_LOAD_PROCEDURE
      /copy errno_h