program define bundle *! bundle png images in a web page using base64 encoding version 11 syntax using/ [, OUTput(string)] mata st_local("infile", addsuffix(`"`using'"', ".html")) confirm file `"`infile'"' capture noisily mata outfile = bundle(`"`infile'"', `"`output'"') local failed = _rc if _rc { forvalues i = 0/20 { capture mata: fclose(`i') } } if `failed' exit mata st_local("outfile", outfile) if `"`outfile'"' == "" display "No png's found" else display `" bundled in `outfile'"' end mata: string scalar bundle(string scalar infile, string scalar outfile) { string vector lines, trio string scalar imgfile real vector matches, bytes real scalar m, fout, i // read html file and find images lines = cat(infile) matches = strmatch(strlower(lines), "* 0 ? outfile : "") } // // insert -b at end of file name // string scalar dashb(string scalar filename) { string name, extension extension = pathsuffix(filename) name = pathrmsuffix(filename) return(name + "-b" + extension) } // Extract src from tag // string vector srcsplit(string scalar line) { real scalar bot, top, m string scalar lc, s, quote lc = strlower(line) m = regexm(lc,"src *= *") assert(m > 0) s = regexs() bot = strpos(lc, s) + strlen(s) quote = substr(lc,bot,1) top = bot + 1 while(top <= strlen(lc) & substr(lc,top,1) != quote) { top++ } return( substr(line,1,bot-1) \ substr(line,bot+1,top-bot-1) \ substr(line,top+1) ) } // // Read file into buffer growing by 10k // real vector getbytes(string scalar filename) { real scalar fh, p real vector bytes string scalar c fh = fopen(filename, "r") bytes = J(1, 10000, 0) p = 0 while( (c=fread(fh,1)) != J(0,0,"")){ p++ if(p > length(bytes)) { bytes = bytes , J(1,10000,0) } bytes[p] = ascii(c) } fclose(fh) return(bytes[1::p]) } // // add suffix to file name if not there // string scalar addsuffix(string scalar name, string scalar suffix) { return(pathsuffix(name) == "" ? name + suffix : name) } // --------------------------------------------------------- // Base 64 encoding string scalar base64(real rowvector bytes) { real scalar nblocks, remainder, nchars, b real rowvector asciis, from, to, tail, hex // split into blocks of 3 plus remainder nblocks = floor(length(bytes)/3) remainder = length(bytes) - 3*nblocks nchars = 4 * nblocks if(remainder > 0) nchars = nchars + 4; asciis = J(1, nchars, 0) // must be row vector // convert each block of 3 bytes to 3 ascii codes from = 1..3 to = 1..4 for(b=1; b <= nblocks; b++) { asciis[to] = b64block(bytes[from]) //char(asciis[to]) from = from :+ 3 to = to :+ 4 } // handle lengths not multiples of 3 (padd = 3 - remainder) if(remainder > 0) { tail = J(1, 3, 0) tail[1..remainder] = bytes[from[1]..length(bytes)] hex = b64block(tail) hex[(remainder+2)..4] = J(1, 3 - remainder, ascii("=")) asciis[to] = hex } // convert ascii codes to a string return(char(asciis)) } // byte to binary bits real vector b64bits(real scalar b) { real rowvector powers, digits real scalar d, p powers = (128, 64, 32, 16, 8, 4, 2) digits = (0, 0, 0, 0, 0, 0, 0, 0) for(p=1; p <= 7; p++) { d = floor(b/powers[p]) if(d > 0) b = b - d * powers[p] digits[p] = d } digits[8] = b return(digits) } // hex encode 6 bits real scalar b64hex(real vector bits) { string scalar map real rowvector powers real scalar r, p map = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789+/" powers = (32, 16, 8, 4, 2, 1) r = 0 for(p=1; p <= 6; p++) { if(bits[p] == 1) r = r + powers[p] } return(ascii(substr(map, r+1, 1))) } // hex encode 3 bytes real vector b64block(real vector w) { real rowvector bits, hex bits = b64bits(w[1]), b64bits(w[2]), b64bits(w[3]) hex = b64hex(bits[1..6]), b64hex(bits[7..12]), b64hex(bits[13..18]), b64hex(bits[19..24]) return(hex) } end