Drawing with tables

WordPress (which runs this blog) is very useful, but it does have its limitations. It would be really nice to be able to specify real styles sheets and scripts. Well, they let you do that if you pay them, which I’m not willing to do, so I shouldn’t complain. But the WordPress system doesn’t allow me to update an image. This is something I want to do frequently — perhaps I’ve found a better shot, or perhaps I have a graph of something that changes with time.

If I upload an image with the same name as a previous image then WordPress appends a “2” (“3”, “4”, etc.) to the internal filename. WordPress does this even if I delete the old version of the file first. So If I have an image that is shared by several pages and I want to update it I must change each and every page to reference the new version.

I haven’t figured out a way to get around that.

I have lots of graphs that change with time. When flowers bloom, when it rains, etc. Potentially every day adds a new datapoint. Often I’ll have one page with many such graphs, such as my record of all the blooms I’ve seen in Santa Barbara where I have a set of graphs for each species. What I want is to embed an image into the page. But WordPress removes embedded <svg> elements, so that doesn’t work.

But I realized I could draw my little calendar graphs with HTML table elements. I could make one image with a yearly calendar showing the months in a linear fashion, use that as a background image for a table, and then divide the year up into table cells representing time when a) the flower was blooming, b) it wasn’t, c) transition between.

2013
<table>
 <tr>
  <td>2013</td>
  <td>
   <table style="background-image:url('http://sbwildflowers.files.wordpress.com/2010/12/cal115-116x19.png');border-spacing:0;padding:0;width:116px;">
    <colgroup> <col style="width: 18px;"> <col style="width: 5px;"> <col style="width: 15px;"> <col style="width: 5px;"> <col style="width: 1px;"> <col style="width: 2px;"> <col style="width: 1px;"> <col style="width: 69px;"> </colgroup>
    <tr style="height:12px;">
     <td></td>
     <td style="background-image:linear-gradient(to right,rgba(0,0,0,0.0),rgba(255,0,0,0.5));"></td>
     <td style="background-color:rgba(255,0,0,0.5);"></td>
     <td></td>
     <td style="background-color:rgba(255,0,0,0.5);padding:0;"></td>
     <td></td>
     <td style="background-color:rgba(255,0,0,0.5);padding:0;"></td>
     <td></td>
    </tr>
    <tr style="height:7px;">
     <td colspan="8"></td>
    </tr>
   </table>
  </td>
 </tr>
</table>
</table>

But I was surprised to discover, fairly quickly, that it didn’t work. The reason being that the default padding for table cells is 1 pixel (in most browsers anyway, though not in the CSS spec). This means that you can’t have a table cell with a width (or height) of 1 pixel; there’s a padding component on each side of the cell, so 2 pixels is the minimum width. So I must set style="padding 0;" on each table cell.

Even that didn’t work. Ah, careful reading of the CSS table spec reveals that there’s something called border-spacing which is placed around cells even if you’ve already said you don’t want borders. OK, so I must set that to 0 too (on the <table> element).

That doesn’t work either. It turns out that the browser doesn’t always use the widths I specify on table cells. I must use <col> as well.

But if I do all those non-obvious things then FireFox displays my little graph the way I want it to be.

Then I wanted to draw a more complicated graph: the total number of species living and blooming on any given day in the recovery zone of a fire.

Number of taxa identifiably alive or blooming
2013

2014
Nov Dec Jan Feb Mar Apr May Jun Jul Aug Sep Oct
<table style="text-align:center;">
<caption>Number of taxa identifiably alive or blooming</caption>
<tr>
 <td>2013<br />-<br />2014</td>
 <td>
  <table style="border:none;border-spacing:0;width:365px;">
   <col span="365" style="width:1px;"/>
   <tr style="height: 13px; padding: 0px;"><td colspan=29 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=27 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=29 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=29 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=29 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=31 style=""></td></tr>
   <tr style="height: 5px; padding: 0px;"><td colspan=29 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=27 style=""></td><td style="padding: 0;background-color: #66f;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=29 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=29 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=29 style=""></td><td style="padding: 0;background-color: #888;"></td><td colspan=31 style=""></td></tr>
   <tr style="height: 1px; padding: 0px;"><td colspan=29 style="padding: 0; "></td><td style="padding: 0;background-color: #888;"></td><td colspan=17 style="padding: 0; "></td><td colspan=4 style="padding: 0; background-color: #0f0;"></td><td colspan=9 style="padding: 0; "></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style="padding: 0; "></td><td style="padding: 0;background-color: #888;"></td><td colspan=27 style="padding: 0; "></td><td style="padding: 0;background-color: #66f;"></td><td colspan=30 style="padding: 0; "></td><td style="padding: 0;background-color: #888;"></td><td colspan=29 style="padding: 0; "></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style="padding: 0; "></td><td style="padding: 0;background-color: #888;"></td><td colspan=29 style="padding: 0; "></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style="padding: 0; "></td><td style="padding: 0;background-color: #888;"></td><td colspan=30 style="padding: 0; "></td><td style="padding: 0;background-color: #888;"></td><td colspan=29 style="padding: 0; "></td><td style="padding: 0;background-color: #888;"></td><td colspan=31 style="padding: 0; "></td></tr>
   ...
  </table>
  </td>
 </tr>
</table>

This works in Firefox, but Safari still makes some table rows have a width of 2 pixels. I think it’s just wrong. (I have only tested in Firefox and Safari).

Of course, once you see that each table cell can be a pixel then you can output any image. It takes about 50 bytes to specify a pixels (instead of 3~4 for an uncompressed binary format, and far fewer for jpeg), but some savings can be made with run length encoding (using colspan when adjacent cells share the same color).

So it should be possible to use this method to draw a full color image

Lilium humboltii flower
Normal image
8K png
Table image
392K text

So here is a little routine which takes an array of pixels and produces a table image. It works in Firefox 🙂

#include <stdio.h>
#include <string.h>

typedef unsigned int	guint;
typedef unsigned char	guint8;

/* This file is in the format produced by gimp for C RGB output */
/* It contains one variable, gimp_image, which is a structure */
/* containing width, height, and pixel_data fields. Pixel_data are */
/* stored as a sequence of bytes, 3 per pixel, the first being the */
/* red value of the first pixel, the second the green value, ... */
#include "Lilium-humboltii-flower2.c"

static char *PixelColor(const guint8 *pixel, char *space) {

    if ( memcmp(pixel,"\ff\ff\ff",3)==0 )
return( "" );
    else if ( memcmp(pixel,"\ff00",3)==0 )
return( "background-color: red;" );

    if ( (pixel[0]&0xf)==((pixel[0]>>4)&0xf) &&
	 (pixel[1]&0xf)==((pixel[1]>>4)&0xf) &&
	 (pixel[2]&0xf)==((pixel[2]>>4)&0xf) )
	sprintf( space, "background-color: #%x%x%x;", pixel[0]&0xf, pixel[1]&0xf, pixel[2]&0xf );
    else
	sprintf( space, "background-color: #%02x%02x%02x;", pixel[0], pixel[1], pixel[2] );
return( space );
}

static void ImageToTable(FILE *file) {
    int r,c, rspan, cspan;
    int byte_width = 3*gimp_image.width;
    const guint8 *rbase;
    char buffer[40], *color;

    fprintf( file, "<table style=\"border: none; border-spacing:0;width: %dpx;\">\n", gimp_image.width );
    fprintf( file,  " <colgroup><col span=%d style=\"width: 1px ! important;\"/></colgroup>\n", gimp_image.width );
    for ( r=0; r<gimp_image.height; r += rspan ) {
	rbase = gimp_image.pixel_data + r*byte_width;
	for ( rspan=1; r+rspan<gimp_image.height; ++rspan ) {
	    if ( memcmp(rbase,
			rbase+rspan*byte_width,
			byte_width)!=0 )
	break;
	}
	fprintf( file, " <tr style=\"height: %dpx;\">\n", rspan );
	for ( c=0; c<gimp_image.width; c+=cspan ) {
	    for ( cspan=0; c+cspan<gimp_image.width; ++cspan ) {
		if ( memcmp(rbase+c*3,
			    rbase+(c+cspan)*3,
			    3)!=0 )
	    break;
	    }
	    color = PixelColor(rbase+c*3,buffer);
	    if ( cspan==1 )
		fprintf( file, "  <td style=\"padding: 0px;%s\"></td>\n", color );
	    else if ( rspan!=1 && *color=='' )
		fprintf( file, "  <td colspan=%d></td>\n", cspan );
	    else
		fprintf( file, "  <td colspan=%d style=\"%s%s\"></td>\n",
			cspan, color, rspan==1? "padding: 0px;": "" );
	}
	fprintf( file, " </tr>\n" );
    }
    fprintf( file, "</table>\n" );
}

int main(int argc, char **argv) {
    ImageToTable(stdout);
return 0;
}
Advertisements

One Response to “Drawing with tables”

  1. Adger Says:

    This is insane, but does it mean you can update your table picture/graph items easily?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: