[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