Thursday, May 28, 2009

OT: Into the void (Blog comments)

In my rush today, I accidentally rejected some recent blog comments. So if you do not see your comment, that is why. So feel free to resubmit them.

Note to self: do not review comments before consuming a full measure of caffeine ;)

...Read More

Thursday, May 14, 2009

ColdFusion - Apache WhoIsClient

I just noticed ColdFusion 8 ships with the Apache (Jakarta) Commons WhoisClient. So if you ever need a WhoIsClient, you might give the built in one a shot.

 
<cfscript>
whois = createObject("java", "org.apache.commons.net.WhoisClient").init();
try {
whois.connect(whois.DEFAULT_HOST);
WriteOutput("<pre>"& whois.query("houseoffusion.com") &"</pre>");
whois.disconnect();
}
catch(IOException e) {
WriteOutput("Error I/O exception: " + e.getMessage());
}
</cfscript>

...Read More

Sunday, May 10, 2009

Java: POI + HWPF + CharacterProperties: Converting colors to RGB or Hex

I have been doing a little work with POI's HWPF lately. Right now I am still trying to grasp the basic structure of binary word documents and wade through the api. As the HWPF project is still in scratchpad mode, the documentation is sparse. So the task is a bit daunting to a novitiate, and after a brief look at the complexity of the Word file format specifications, I have an even greater appreciation for the work of POI developers.

Anyway, one of the first puzzles I ran into involved colors. I have been using the CharacterProperties class, and as its name implies, it stores the characteristics of a series of characters (such as text color, font size, bolding, etcetera). Unfortunately, reading or changing the text color was not as straight-forward as I thought.

CharacterProperties has at least two ways of setting the text color: setColor(int) and setIco24(int). The setColor method expects what is called an "ico". Apparently an "ico" is not a color, but an index representing an entry in a color palette. Looking at the source, that would seem to be the old 16 color palette from the days of Word 97.


case 0: // auto
return -1;
case 1: // black
return 0x000000;
case 2: // blue
return 0xFF0000;
case 3: // cyan
return 0xFFFF00;
case 4: // green
return 0x00FF00;
... etcetera


Obviously, that is too limiting so I took a look at the setIco24 method. That seemed to be right way to set the color for Word 2000+ documents which have 24 bit color. So I tried passing in a Color.getRGB() value, but no joy. The problem was Word represents colors (or COLORREF's) in 0X00BBGGRR (blue green red) form, and not the standard 0x00RRGGBB (red green blue). So that explained why my attempt to use an rgb color failed.

Since I am accustomed to working with rgb format, I decided to rig up a few convenience methods to make it easier to work with Word colors. One caveat, I was not sure how to determine the color represented by "Automatic". So for now the methods return black for "Auto".
  • rgbToIco24 - Converts an rgb value to a Word COLORREF
  • hexToIco24 - Converts a hexadecimal color to a Word COLORREF
  • ico24ToRGB - Returns the rgb values of a Word COLORREF
  • ico24ToHex - Returns rgb hexadecimal value of a Word COLORREF

Bear in mind I am still in the learning mode. So the usual disclaimers apply: These are just my impressions so far, not gospel. Anyway, the methods seem to be working well. They need further testing. But if the code holds up, maybe I will suggest it as an addition on the POI list. In case someone else finds it useful.


import java.awt.Color;

/**
* @author http://cfsearching@blogspot.com
*
* Convert CharacterProperties.getIco24() values to RGB or Hex
*
* Version:
* JDK 1.6.0_13
*/
public class ConvertIco24ToRGB {

/**
* Converts a CharacterProperties.getIco24() value into rgb format.
* Ico24 (or ColorRef) values, have the hexadecimal form: 0x00BBGGRR.
* (Applies to binary file formats only)
*
* Note, getIco24() returns -1 to represent "Auto". It is not clear how
* to determine that color value. So for now, the color "black" is used for "Auto".
*
* @param ico24 - Word colorRef value returned by CharacterProperties.getIco24()
* @return Array of color values [0] red, [1] green, [2] blue
*/
public static int[] ico24ToRGB(int ico24) {
int[] rgb = new int[3];

// If the colorRef is not "Auto", unpack the rgb values
if (ico24 != -1) {
rgb[0] = (ico24 >> 0) & 0xff; // red;
rgb[1] = (ico24 >> 8) & 0xff; // green
rgb[2] = (ico24 >> 16) & 0xff; // blue
}

return rgb;
}

/**
* Convenience method. Returns the ico24 value as as hexadecimal string, form: 0x00BBGGRR
* @param ico24 - Word colorRef value returned by CharacterProperties.getIco24()
* @return hexadecimal string in form: 0x00BBGGRR
*/
public static String ico24AsHex(int ico24) {
return Integer.toHexString(ico24);
}

/**
* Converts a CharacterProperties.getIco24() value to rgb hexadecimal format.
* Ico24 (or ColorRef) values, have the hexadecimal form: 0x00BBGGRR.
* (Applies to binary file formats only)
*
* @param ico24 - Word colorRef value returned by CharacterProperties.getIco24()
* @return hexadecimal color. Returns 0 if it cannot be determined
*/
public static String ico24ToHex(int ico24) {
int r = (ico24 >> 0) & 0xff; // red;
int g = (ico24 >> 8) & 0xff; // green
int b = (ico24 >> 16) & 0xff; // blue
int rgb = (r << 16) | (g << 8) | b;

// Find a better way to maintain leading zeroes
return Integer.toHexString(0xC000000 | rgb).substring(1);
}

/**
* Converts a six digit hexadecimal value (RRGGBB) to a CharacterProperties.setIco24(int)
* compatible value. Ico24 (or ColorRef) values, have the hexadecimal form: 0x00BBGGRR.
* (Applies to binary file formats only)
*
* @param hexColor - Word colorRef value returned by CharacterProperties.getIco24()
* @return colorRef value
*/
public static int hexToIco24(String hexColor) {
if (hexColor == null || hexColor.length() != 6) {
throw new IllegalArgumentException("hexColor must be 6 characters in length. Example: ffffff");
}
int r = Integer.parseInt(hexColor.substring(0, 2), 16);
int g = Integer.parseInt(hexColor.substring(2, 4), 16);
int b = Integer.parseInt(hexColor.substring(4, 6), 16);
return rgbToIco24(r, g, b);
}

/**
* Converts an rgb color to a Word colorRef value that should be compatible with
* CharacterProperties.setIco24(int). Ico24 (or ColorRef) values, have
* the hexadecimal form: 0x00BBGGRR. (Applies to binary file formats only)
*
* @param r red
* @param g green
* @param b blue
* @return ico24 (or ColorRef) value
*/
public static int rgbToIco24(int r, int g, int b)
{
return (0xff << 24) | (b << 16) | (g << 8) | r;
}



public static void main(String[] args) {
String inputPath = "input/blank.doc";
String outputPath = "output/InsertStyledTextA.doc";
inputPath = "output/InsertStyledText.doc";

try {
System.out.println("Test Color");
System.out.println("=======================");

Color c = Color.BLACK;
// Extract the hex and rgb values of the selected color
int r = c.getRed();
int g = c.getGreen() ;
int b = c.getBlue();
String hex = Integer.toHexString(c.getRGB() | 0x000000).substring(2);
System.out.printf("red=%d green=%d blue=%d | hex= %s \r", r, g, b, hex);


// Convert the rgb values to a ColorRef
int ico24 = rgbToIco24(r, g, b);
// Display parameters and result
System.out.printf("rgbToIco24(red=%d, green=%d, blue=%d) = %d \r", r, g, b, ico24);

// Convert the hex color to a ColorRef
ico24 = hexToIco24(hex);
System.out.printf("hexToIco24(hex=%s ) = %d \r", hex, ico24);

// Convert the ColorRef back to rgb
int[] rgb = ico24ToRGB(ico24);
System.out.printf("ico24ToRGB(ico24=%d) = red=%d, green=%d, blue=%d \r", ico24, rgb[0], rgb[1], rgb[2]);

// Convert the ColorRef back to hex
hex = ico24ToHex(ico24);
System.out.printf("ico24ToHex(ico24=%d) = hex=%6s \r", ico24, hex);

}
catch (Exception e)
{
e.printStackTrace();
}

}

}

...Read More

Thursday, May 7, 2009

ColdFusion: Debugging CFPDFForm Information (It's never enough)

Previously, I mentioned some gotchas with handling checkboxes and radio buttons with cfpdfform. One of the most common being, using the wrong "value". Which got me to wondering, how would you verify the values are correct or incorrect, other trial and error?

Using <cfpdform action="read" ..> only returns a field's current value. It does not return all of the possible options for fields like lists, checkboxes, etcetera. But you can use iText's AcroFields class to access more detailed information about Acrobat form fields. Using the AcroFields metadata, you could easily put together a great debugging function

There is a good basis for a function in one of the great examples on the iText site. Using it as a foundation, I whipped a quick function and voila: an array of detailed field information like field type, list options/values, page numbers, coordinates, etcetera.



But it could easily be modified to return fewer or more details, if desired. Ahh, the beauty of iText.


<!---
USAGE
--->
<cfset results = pdfFormFieldDump( ExpandPath("./register_form1.pdf") )>
<cfdump var="#results#" label="pdfFormFieldDump">

<!---
FUNCTION
--->
<cffunction name="pdfFormFieldDump" returntype="array">
<cfargument name="path" type="string" required="true">

<cfset var i = "" >
<cfset var prop = "" >
<cfset var name = "" >
<cfset var type = "" >
<cfset var reader = "" >
<cfset var positions = "" >
<cfset var fieldData = "">
<cfset var AcroFields = "" >
<cfset var formData = "" >
<cfset var fieldNames = "" >

<cfscript>
// read in the pdf file and get the form field metadata
reader = createObject("java", "com.lowagie.text.pdf.PdfReader").init( arguments.path );
AcroFields = createObject("java", "com.lowagie.text.pdf.AcroFields");
formData = reader.getAcroFields();
fieldNames = structKeyArray(formData.getFields());

// extract the properties of each field
fieldData = arrayNew(1);
for (i = 1; i lte arrayLen(fieldNames); i = i +1)
{
// get the current field
name = fieldNames[i];
type = formData.getFieldType( name );

// initialize a new structure for storing this field's properties
prop = structNew();
prop.fieldName = name;
positions = formData.getFieldPositions( name );
prop.page = positions[1];
prop.llx = positions[2];
prop.lly = positions[3];
prop.urx = positions[4];
prop.ury = positions[5];

// store the field type and properties
if ( type eq AcroFields.FIELD_TYPE_CHECKBOX )
{
prop.type = "Checkbox";
prop.states = formData.getAppearanceStates( name );
}
else if ( type eq AcroFields.FIELD_TYPE_COMBO )
{
prop.type = "Combobox";
prop.options = formData.getListOptionExport( name );
prop.values = formData.getListOptionDisplay( name );
}
else if ( type eq AcroFields.FIELD_TYPE_LIST )
{
prop.type = "List";
prop.options = formData.getListOptionExport( name );
prop.values = formData.getListOptionDisplay( name );
}
else if ( type eq AcroFields.FIELD_TYPE_NONE )
{
prop.type = "None";
prop.value = formData.getField( name );
}
else if ( type eq AcroFields.FIELD_TYPE_PUSHBUTTON )
{
prop.type = "Pushbutton";
prop.value = formData.getField( name );
}
else if ( type eq AcroFields.FIELD_TYPE_RADIOBUTTON )
{
prop.type = "Radiobutton";
prop.states = formData.getAppearanceStates( name );
}
else if ( type eq AcroFields.FIELD_TYPE_SIGNATURE )
{
prop.type = "Signature";
}
else if ( type eq AcroFields.FIELD_TYPE_TEXT )
{
prop.type = "Text";
prop.value = formData.getField( name );
}
else
{
prop.type = "Unknown";
prop.value = formData.getField( name );
}

// save the properties in the main array
arrayAppend( fieldData, prop );
}
</cfscript>

<cfreturn fieldData >
</cffunction>

...Read More

Populating CFPDFForm (Much ado about buttons and boxes)

I saw an interesting question on the adobe forums about populating a form with checkboxes. The question was how to check those darn checkboxes.

Handling checkboxes (or radio buttons for that matter) is not that different than populating text fields. Take the sample form below, from the itext site. Nothing fancy, just a simple form with some buttons and checkboxes. Initially, all of the boxes are un-checked. But with a few cfpdfformparam tags, you can easily mark them all as checked.


Source: iText in Action - Chapter 15 (Buttons)
Sample PDF: buttons.pdf


<!--- check all three checkboxes: Dutch, English, French --->
<cfpdfform action="populate" source="#ExpandPath('buttons.pdf')#">
<cfpdfformparam name="Dutch" value="On">
<cfpdfformparam name="English" value="On">
<cfpdfformparam name="French" value="On">
</cfpdfform>

I suspect the biggest gotcha is using the wrong "value". If you use an invalid value like "WhatTheHeckGoesHereToCheckThisDarnBox", when the form is expecting On/Off, obviously the box will not be checked.

Not to mention the fact that the "value" seems to case-sensitive. At least as far as I can tell. So if you accidentally used a lowercase "on" instead of "On", again the box will not be checked.

<!---
this FAILS because the expected "value" is case sensitive
ie The expected values are "On" not lowercase "on"
--->
<cfpdfform action="populate" source="#ExpandPath('buttons.pdf')#">
<!--- check all three checkboxes: Dutch, English, French --->
<cfpdfformparam name="Dutch" value="on">
<cfpdfformparam name="English" value="on">
<cfpdfformparam name="French" value="on">
</cfpdfform>

Just things to watch out for in the wonderful world of pdf forms.



...Read More

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep