Archive for the ‘Useful Information’ Category

Java CMYK HOWTO

Monday, November 24th, 2008

Lately, I’ve been playing with printers-trying to control the amount of cyan, magenta, yellow, and black printed by the printer. For various reasons I’ve been doing it in Java,and I’ve run up against some brick walls. There is no good HOWTO available, so I decided to write my own…

Java supports RGB by default and it works well, but it treats everything else like the proverbial red headed step child. To get it to work you have to find an ICC_Profile (CMYK.pf), load the file dynamically, and then do all your image manipulation. I never looked far enough to find out for sure, but I believe you have to do some special work to get it to save as CMYK also. It’s a pain.

The other option is to extend ColorSpace to support CMYK. You still have to save in a funny way, but this is what I decided to do.

First, the ColorSpace extension (available here, note that the code here is all GPL):

public class CMYKColorSpace extends ColorSpace implements Serializable {

	private static final long serialVersionUID = -5982040365555064012L;

	/**
	 * Create a new CMYKColorSpace Instance.
	 */
	public CMYKColorSpace() {
		super(ColorSpace.TYPE_CMYK, 4);
	}

	/**
	 * Converts to CMYK from CIEXYZ. We cheat here, using the RGB colorspace
	 * to do the math for us. The toCIEXYZ function has a description of how
	 * this is supposed to work, which may be implemented in the future.
	 *
	 * @see java.awt.color.ColorSpace#fromCIEXYZ(float[])
	 * @see org.scantegrity.lib.CMYKColorSpace#toCIEXYZ
	 */
	@Override
	public float[] fromCIEXYZ(float[] p_colorvalue) {
		ColorSpace l_cs = ColorSpace.getInstance(ColorSpace.TYPE_RGB);
		float[] l_rgb = l_cs.toCIEXYZ(p_colorvalue);
		return fromRGB(l_rgb);
	}

	/**
	 * Converts a given RGB to CMYK. RGB doesn't really use black, so K will
	 * always be 0. On printers, the black should actually look dark brown.
	 * RGB (an additive space) is simply the backwards from CMY (a subtractive
	 * space), so all we do is:
	 *
	 * 		C = 1-R
	 * 		M = 1-G
	 * 		Y = 1-B
	 *
	 * @param p_rgbvalue - The color to translate
	 * @return a float[4] of the CMYK values.
	 * @see java.awt.color.ColorSpace#fromRGB(float[])
	 */
	@Override
	public float[] fromRGB(float[] p_rgbvalue) {
		/* TODO: Maybe we should do a better job to determine when black should
		 * be used and pulled out? -- At this time, it's not necessary for our
		 * (Scantegrity's) uses.
		 */
		float[] l_res = {0,0,0,0};
		if (p_rgbvalue.length >= 3) {
			l_res[0] = (float)1.0 - p_rgbvalue[0];
			l_res[1] = (float)1.0 - p_rgbvalue[1];
			l_res[2] = (float)1.0 - p_rgbvalue[2];
		}
		return normalize(l_res);
	}

	/**
	 * Converts the CMYK color to CIEXYZ. Because CIEXYZ is 3-component, we
	 * cheat, converting to RGB and then using the RGB colorspace function
	 * to do the conversion. Details on this colorspace are available on
	 * wikipedia:
	 *
	 * http://en.wikipedia.org/wiki/CIE_XYZ_color_space
	 *
	 * There is also an "ideal relationship" to CMYK, which might be implemented
	 * in the future (don't recall the reference we got this from, probably
	 * color.org):
	 *
	 * C = (C' - K)/(1 - K)
	 * M = (M' - K)/(1 - K)
	 * Y = (Y' - K)/(1 - K)
	 * K = Min(C', M', Y')
	 *
	 * X   41.2453 35.7580 18.0423 | 1-C'
	 * Y = 21.2671 71.5160 07.2169 | 1-M'
	 * Z   01.9334 11.9193 95.0227 | 1-Y'
	 *
	 * @see java.awt.color.ColorSpace#toCIEXYZ(float[])
	 */
	@Override
	public float[] toCIEXYZ(float[] p_colorvalue) {
		float[] l_rgb = toRGB(p_colorvalue);
		ColorSpace l_cs = ColorSpace.getInstance(ColorSpace.TYPE_RGB);
		return l_cs.toCIEXYZ(l_rgb);
	}

	/**
	 * Converts CMYK colors to RGB. Note that converting back will be lossy. The
	 * formula for this is:
	 *
	 * K = 1 - K (go to additive)
	 * R = K * (1 - C)
	 * G = K * (1 - M)
	 * B = K * (1 - Y)
	 *
	 * @param p_colorvalue The color in CMYK.
	 * @see java.awt.color.ColorSpace#toRGB(float[])
	 */
	@Override
	public float[] toRGB(float[] p_colorvalue) {
		float[] l_res = {0,0,0};
		if (p_colorvalue.length >= 4)
		{
			float l_black = (float)1.0 - p_colorvalue[3];
			l_res[0] = l_black * ((float)1.0 - p_colorvalue[0]);
			l_res[1] = l_black * ((float)1.0 - p_colorvalue[1]);
			l_res[2] = l_black * ((float)1.0 - p_colorvalue[2]);
		}
		return normalize(l_res);
	}

	/**
	 * Normalize ensures all color values returned are between 0 and 1.
	 *
	 * @param p_colors
	 * @return p_colors, with any values greater than 1 set to 1, and less than
	 * 0 set to 0.
	 */
	private float[] normalize(float[] p_colors) {
		for (int l_i = 0; l_i < p_colors.length; l_i++) {
			if (p_colors[l_i] > (float)1.0) p_colors[l_i] = (float)1.0;
			else if (p_colors[l_i] < (float)0.0) p_colors[l_i] = (float)0.0;
		}
		return p_colors;
	}
}

The color space is used to convert CMYK to RGB or CIEXYZ on demand, which is done whenever Java displays images on the screen, saves them, or copies them to any context in which the color space is different. It also allows you to create CMYK colors and to store the data in a custom color model/sample color model.

Creating CMYK Colors

This is self explanatory:

float[] l_colorComponents = {1, 0, 0, 0};
CMYKColorSpace l_cs = new CMYKColorSpace();
Color l_cyan = new Color(l_cs, l_colorComponents, 1);

Creating a BufferedImage with CMYK Colors

This piece is the most complicated becase you need to understand how Java models the colors in an Image Object. You may want something in-depth, but here’s a short explanation: Each color in java is represented as a “band,” and it is stored in a way defined by Model and stored in a generic “DataBuffer” object.  To get it working you create a (usually component) ColorModel, and then create a sample model with the specific layout (Interleaved, banded or one color per, etc). The sample model creates a Raster, which then creates the Image object.

CMYKColorSpace l_cs = new CMYKColorSpace();
ComponentColorModel l_ccm = new ComponentColorModel(l_cs, false, false,
						1, DataBuffer.TYPE_FLOAT);
int[] l_bandoff = {0, 1, 2, 3}; //Index for each color (C, is index 0, etc)
PixelInterleavedSampleModel l_sm = new PixelInterleavedSampleModel(
						   DataBuffer.TYPE_FLOAT,
						   (int)l_width, (int)l_height,
					     	   4,(int)l_width*4, l_bandoff);
WritableRaster l_raster = WritableRaster.createWritableRaster(l_sm,
							new Point(0,0));
BufferedImage l_ret = new BufferedImage(l_ccm, l_raster, false, null);

Graphics2D l_g2d = l_ret.createGraphics();

Saving CMYK Images

For this, I chose to use the iText library to save in PDF format. All you have to do is pull the DataBuffer out of the raster and save the bytes. You might also want to save in JPEG or another kind of format, which should be reasonably easy (just send the function the right bytes in the right order) once you’ve seen below:

Raster l_tmpRaster = c_img.getRaster();
DataBuffer l_db = l_tmpRaster.getDataBuffer();
byte[] l_bytes = new byte[l_db.getSize()];
for (int l_i = 0; l_i < l_bytes.length; l_i++) {
	l_bytes[l_i] = (byte)Math.round(l_db.getElemFloat(l_i)*(float)255);
}

com.lowagie.text.Image l_img = com.lowagie.text.Image.getInstance(
								l_tmpRaster.getWidth(),
								l_tmpRaster.getHeight(),
								4, 8, l_bytes);
l_img.setDpi(300, 300);
Document l_doc = new Document(new Rectangle(0,0,l_img.getWidth(), l_img.getHeight()));
l_doc.setMargins(0,0,0,0);
PdfWriter.getInstance(l_doc,
			new FileOutputStream("inkeratorout" + c_c + ".pdf"));
l_doc.open();
l_doc.add(l_img);
l_doc.close();

Everything else should work as normal (just remember that it is calling the colorspace functions to do it’s job).

I wrote this in a bit of a rush (i’m pretty busy today). I haven’t had any problems with it yet, but if you do please feel free to leave a note below.d

Dealing with Duplicate SQL Rows

Tuesday, October 28th, 2008

I was using the export/import functions in Wordpress to transfer stories to a new (clean) installation, and the import was causing a duplicate row problem in the wp_postmeta table which was causing the multiple authors plugin to list the importing user dozens of times for each story. My quick search yielded nothing, so I pulled out my arcane SQL knowledge and used some MySQL documentation to come up with this:

> create temporary table newmeta as select distinct post_id,meta_key,meta_value from wp_postmeta;

> truncate wp_postmeta;

> insert into wp_postmeta(post_id, meta_key, meta_value) select post_id,meta_key,meta_value from newmeta;

> delete from wp_postmeta where meta_key = ‘other_author’ and meta_value = ‘admin’;

> delete from wp_postmeta where meta_key = ‘other_author’ and meta_value = ”;

Note that you need to specify the column names that you know will reproduce the duplicate and you can’t use *. I had to do some additional cleanup because the import set ‘admin’ to be an author on every story, and produced some blank entries.

I also had to modify the multiple authors plugin due to this error that kept cropping up:

Warning: Invalid argument supplied for foreach() in
/blog/wp-content/plugins/multiple-authors.php on
line 46

Basically, I just made sure it checked that the variables it was using weren’t blank before it got to the foreach statement:

if ( $other_authors == “”) return; //INSERTED Line

foreach ($other_authors as $author) {

Pretty annoying, but not too hard to fix.

Changing Default xterm colors on OSX X11

Thursday, October 23rd, 2008

This is a question that I am asked about constantly, and it is really simple: Edit the file /usr/X11R6/lib/X11/app-defaults/XTerm and add the following:

XTerm*background: black
XTerm*foreground: white

I usually do it after the savelines option. Most other names work, too (yellow, cyan, etc). For more info, you can check the manpage on xfree86.org and Thomas Dickey’s xterm site.

Gizmo Settings

Tuesday, February 12th, 2008

This is mostly for my own reference, but I’m letting it be public on the off chance someone stumbles upon this blog searching for this information.

Pidgin Setup:

Username: <gizmo username>
Domain: chat.gizmoproject.com
Protocol: XMPP (Jabber)

That should be all you need, although my buddy list is empty except alice and she is not online.

SIP Client Setup:

User name: <gizmo phone number>
Domain: proxy01.sipphone.com
Registrar: proxy01.sipphone.com
STU server: stun01.sipphone.com

For whatever reason, you will need to make your password lowercase here. It didn’t work when I used my regular password.

The Beauty of Redundancy

Wednesday, February 6th, 2008

I saw this post over at election updates and this piece piqued my interest:

Also, I talked to an election official here in Utah who noted that if that happened here and someone stole the vote cartridges, where we use DREs, they would download the votes directly from the voting machines. Also, the thief would have to steal the paper trails too, which are in a different sealed bag.

Redundancy is a good thing. You could achieve something similar with a PCOS machine.

Microkernels vs Monolithic Kernels

Thursday, January 31st, 2008

Via slashdot, Tanenbaum has created a new chapter in the debate between which is better. It’s a great read, even for those not familiar with the debate. Here’s a choice quote:

If you are wondering why microkernels aren’t even more widely used, well, there is a lot of inertia in the system. Why haven’t Linux or Mac OS X replaced Windows? Well, there is a lot of inertia in the system. Most cars in Brazil can run on home-grown ethanol so Brazil uses relatively little oil for vehicle fuel. Why doesn’t the U.S. do that and reduce its dependence on the volatile Middle East? Well, there is a lot of inertia in the system. Getting people to change, even to superior practices, is very hard.

I have always been a fan of the microkernel idea because it enforces a logical separation among components that makes it easier build securely and test an operating system. It is also useful, when you are working on something, to get a sense that there is a clear boundary. In a microkernel, there are no shared data structures across the whole kernel, so if you don’t like the way a module uses it or how the module is designed, you can wipe it out and not worry about breaking other parts of the system. This is a huge plus. Much of why and how we managed to get computers to do such complicated things is based on the idea of building small, simple pieces, and then building more complicated things that use those pieces. A microkernel is basically doing the same thing with operating system concepts.

He also mentions MINIX 1, which, a long time ago when I was an undergraduate I had many chances to play with. Apparently, there is now a MINIX 3. I must admit that right now I am really fighting the urge to download and try it over finishing my thesis… Maybe later.

Clever use of strace

Tuesday, January 29th, 2008

I found a clever use of strace that gives “cp” a progress bar. I will have to keep this in my bag of tricks.

Fun with Invisible Ink

Saturday, January 26th, 2008

One of my favorite toys when I was younger were invisible ink pens. You could write with them anywhere, and once the ink dried, you needed a black light or another special revealing pen to see your message again. I remember using them them for many things:

  • Hiding the key for my (poorly) coded messages
  • The locations of objects I had hidden
  • A personal diary (that lasted about a week)
  • Drawing unflattering pictures of my siblings, and flattering pictures of myself
  • Putting my name on things I owned (and didn’t own), so I could prove that I owned them to my dad when my siblings took them
  • Passing secret messages in class

The list goes on and on..Of course, the type of invisible ink I am referring to is the chemical reaction kind. There are also inks that react with heat, or that can only be seen under certain lights. Today I came across a great use of heated invisible ink. A food company ships its annual report printed in this ink. In order to read it, you have to “bake” the book properly. Very clever!