Letter Positioning

This script was created by Dedric Mauriac in January 2006 (Earliest date found on Letter Board 1.2 scripts). It works similar to that of XyText_1.5 in that it displays up to 10 letters in a single prim.
1. Your prim description needs to be "ROW;COLUMN" format. Rows start at zero. Each prim is considered as a column (even though they show 10 letters).
2. You need to reset the script if you change the description.
3. Number of letters available are only the lower ASCII printable characters. See XyText_1.5 or XyzzyText if you need to support more characters.
In order to display text, you must send a link message to the link set indicating the row index, and the text command.
llMessageLinked(LINK_SET, rowIndex, "text;Hello World!", NULL_KEY);
You may also change the color of the text with the color command and the row index.
llMessageLinked(LINK_SET, rowIndex, "color;<1,0,0>", NULL_KEY);
integer globalRow;
integer globalColumn;
string fontName = "Lucida Console"; // determines what notecard to lookup information from
string characters = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
integer columns = 9; // number of characters per row
integer rows = 11; // number of rows per image
integer lastRows = 0; // number of rows in last image
list pairText = ["", "", "", "", ""]; // keeps track of what is currently displayed
list pairImage = [-1, -1, -1, -1, -1]; // keeps track of what image to use
list images = []; // UUID of all images of font
string message = " ";
string delimiter = ";";
float width;
float innerWidth; // width without left and right margins
float height; // percent each row takes up in image
float lastHeight; // percent of each row takes up in last image
float lastVOffset;
float vOffset;
float hOffset;
integer lastSet;
list prim;
integer mapSide(integer index)
{
 // return the face index for the side provided.
 // values are for a hollow cut prism
 if(index == 0) return 3;
 if(index == 1) return 7;
 if(index == 2) return 4;
 if(index == 3) return 6;
 if(index == 4) return 1;
 return -1;
}
showText(string text)
{
 // if text too long, truncate it
 if(llStringLength(text) > 10) text = llGetSubString(text, 0, 10);
 // if text not long enough, append spaces
 while(llStringLength(text) < 10) text += " ";
 
 prim = [];
 
 // loop through each pair of characters
 integer i;
 for(i = 0; i < 5; i++)
 {
 // parse charcter pair from text
 string pair = llGetSubString(text, i * 2, (i * 2) + 1);
 
 // show the pair on the designated side
 showTextOnSide(pair, i);
 }
 if(llGetListLength(prim) != 0) 
 llSetPrimitiveParams(prim);
}
showTextOnSide(string text, integer side)
{
 // if text is already shown on this side, no further action is necessary
 if(llList2String(pairText, side) == text) return;
 
 // remember new text for next time
 llListReplaceList(pairText, [text], side, side);
 integer value = baseValue(text);
 integer set = llFloor(value / (columns * rows));
 integer tempValue = value - (set * columns * rows);
 integer row = llFloor(tempValue / columns);
 integer column = (tempValue - (row * columns));
 integer face = mapSide(side);
 
 key texture = llList2Key(images, set);
 vector size = getSize(set, side);
 vector offset = getOffset(set, row, column, side);
 prim += [PRIM_TEXTURE, face, texture, size, offset, 0];
}
vector getOffset(integer set, integer row, integer column, integer side)
{
 vector offset = <0,0,0>;
 offset.x = hOffset + column * width;
 offset.x -= width * 0.08333;
 
 if(set == lastSet)
 offset.y = lastVOffset - row * lastHeight;
 else
 offset.y = vOffset - row * height;
 offset.y += height * .1;
 
 if(side == 0) offset.x += width * .54;
 else if (side == 1) offset.x -= width * .05;
 else if (side == 2) offset.x -= width * 5.7;
 else if (side == 3) offset.x -= width * .05;
 else if (side == 4) offset.x -= width * .65;
 
 if(offset.x > 1.0) offset.x -= 1.0; 
 else if(offset.x < -1.0) offset.x += 1.0;
 
 if(offset.y > 1.0) offset.y -= 1.0; 
 else if(offset.y < -1.0) offset.y += 1.0;
 
 return offset;
}
vector getSize(integer set, integer side)
{
 if(set == lastSet)
 {
 if(side == 0 || side == 4) return <innerWidth * 2.5, lastHeight, 0>;
 else if(side == 2) return <innerWidth * -16.666, lastHeight, 0>;
 else return <innerWidth, lastHeight, 0>;
 }
 else
 {
 if(side == 0 || side == 4) return <innerWidth * 2.5, height, 0>;
 else if(side == 2) return <innerWidth * -16.666, height, 0>;
 else return <innerWidth, height, 0>;
 }
}
integer baseValue(string text)
{
 integer base = llStringLength(characters);
 integer num = llSubStringIndex(characters, llGetSubString(text, 0, 0)) * base;
 num += llSubStringIndex(characters, llGetSubString(text, 1, 1));
 return num;
}
init()
{
images = [
"e8a6022f-7447-d2e6-49a8-b494d62d5886", "bc06ac3c-b52c-8002-77ed-a47a854b2974", "e4457ea8-922d-9b96-66f1-407152fc9c40",
"6ae0aa0b-2bc8-ee28-d38c-7c5afbeb4a69", "bac54c4e-4dbc-0c93-53f2-4a53d807ccac", "f2d823eb-a3d0-79ca-4c7d-72106249eb00", 
"cae27783-f95e-cdbd-991e-76a51ad02a76", "65f4bcf0-172e-7e3e-a175-1429f3e1c7c6", "633e2086-2d7c-2539-1bcb-7a1ba7b9e6e6", 
"ca481e1e-2a5e-bfb9-d777-01923e4e3838", "a8b3c485-4697-28fc-34cd-6a7afe92c053", "a7474832-ac05-8109-af00-91ed8aa9efdd", 
"cb45883b-dd43-010a-4b6f-dbafe56ae241", "26a13d39-eebb-3840-1fff-96842e3b0b0f", "5dafcd3e-be2a-43d6-c407-781bd132cc65", 
"0b968b04-ee67-0fa3-e5a3-eee313fa9f38", "1afb9c1c-4b19-cf22-0a76-4da4ee3de4b9", "ca09b4ea-a731-d8f0-b887-7e8de54b4d95", 
"48b13bf0-939d-d8ba-9c40-bdb3e9535708", "fbdc23ac-b5da-7ac6-a4a5-16c241f2b242", "2584c008-20ec-9ebe-0ec7-9edeecc6e002"];
images += [ 
"3db97303-5416-1d8e-585f-1b056ad2593c", "afd2c644-4791-0b6e-ce94-23f6954586df", "db323f1a-096e-f211-c94c-458f4250fd78", 
"221c9458-25dc-b2cd-22d0-02f2a08ad332", "60d8ea8a-4706-e517-c709-d44cb2f70f63", "5741bde0-1407-7b13-5ccc-86d44f3d0629", 
"6978c82a-d38e-576f-549b-c14cb06577ac", "9398f35b-f951-5d19-109c-5e21299df950", "cfa54236-bfbe-b59e-f360-072c697cdbd9", 
"91a14d85-1df7-42bc-7343-28e2ac22a89e", "9aa62d16-d5b5-9d20-59ab-448278152c34", "755a74fe-4bda-c682-a9da-58f6a4f1ad70", 
"2d2e9c4d-a521-2fb3-7907-7ca7e5696c98", "0e573e63-9a75-1f0d-c830-78a7f37e4df4", "426bcf2e-b842-a3a3-1521-89498cb67cb5", 
"153620b6-e276-d8b3-1efd-2223028cc8f0", "fd275b06-42e5-e93e-fc34-6f281a1985f3", "45840424-bb73-78f9-4689-277bd810b125", 
"25c4f66d-bff0-f059-cc56-11528ef518f3", "7e014232-0095-9c0d-975f-1eeb9d2ce5af", "1efa0dfe-6c25-ad2c-a890-9072214f7847", 
"83b52b04-704f-3bed-ec27-7a14717e376b", "1390ef19-7bc9-ec9b-bf4e-97becd4ec36c", "e9afccbb-b79a-bf85-7faa-9a6a95c0a3bc", 
"ba8ded30-1624-2c34-e800-e6c5e5106b87", "d6d25fb8-500b-9859-4148-57fee9f59113", "0c370927-0752-b40b-afa8-23ce2aee3382", 
"db6d27ed-88c6-7244-f2bc-a3b7e90efa43", "02f72ba7-7daf-80dd-128f-1abccadccf1e", "2b8ebf28-3a90-2c82-e3b6-c067c44704ba", 
"5aa06213-f0e6-4356-2958-9a4321f374e6", "2073a851-7afe-3ea2-9902-5edfa6f3c4dd", "942763a1-984d-7977-89ba-3760f15e051e", 
"89fce16e-7ef7-4cb4-49f3-a92a40a2f696", "e2259cee-b1f1-0fa3-3d1f-071e76c06c5d", "d01816a6-4463-0b4c-5de7-b4501b1586aa", 
"476322e9-5245-c99f-7929-d1e48553bba2", "85b73391-0504-7a9f-754c-69afe9d8a60e", "e905b435-e10a-eaf1-6917-04ce559aaead"];
images += [ 
"b769d41b-b3d8-c16b-0bfa-a2a6fea15ea5", "65769a55-5c00-02d8-5e64-27cf9bc8005b", "a9dea7cf-63a3-a4e8-9857-e4d88cab334d", 
"13abaee5-a730-bdf3-9d33-8336a31b0e23", "e03f1c9f-f64b-85fd-e439-ab402d75b54d", "700ada18-367e-acc1-07a4-c89e0f0895af", 
"f6e1a6e0-4851-606e-56b2-62a616e7413e", "283b7cba-b819-322d-f1e9-5fededd10eab", "36e045eb-e8bc-52ee-7ec7-acd2ee8d1158", 
"40ad6793-49b3-a7a6-140f-4c4a3c021e2f", "a5afb13a-8491-a914-9c92-38a710ca1996", "7cdd72eb-cbd9-5f52-1502-5bb011633748", 
"883f74f0-58c4-c3eb-7e94-1fb9322e478f", "b1b7c584-f028-0082-3f61-f977e7b4e8cc", "9446bbf7-5bd8-daaa-78b7-f05b9f9b3af3", 
"17c9d5e0-1c59-331d-ab6a-13385c074ebc", "596db9f4-38e6-0ff9-1aae-38c9cdd5e1b6", "2b19d5c6-5085-99aa-ca10-212ee1a4b047", 
"38cf3e3c-00ab-833b-a5c2-61e6f3a76751", "697d40ce-dc51-96d4-530a-d74343658542", "0d3916cb-dbad-d3e3-28b4-be68deb2d2dd", 
"f218385a-6c6d-1ff8-6b9e-fad3781c767b", "4c3a2035-de3a-12b2-c5e9-7319871a7434", "6ac3c2cf-3454-a160-075e-273410897a60", 
"f42f28c4-e903-3a65-2532-dbbaf5b742b9", "a60206b4-84e9-6663-6116-cc930ca0f984", "9879a388-0a84-ae07-d28f-9f0d87e85fd7", 
"68a342f2-96b2-1a4e-a538-ea4bfc5f4eb0", "adb0e8cf-f1c7-d025-8080-0d4046dc68a4", "3a75dc08-43e9-431d-4c89-1fe968aa3b46", 
"1d4e3f85-02e3-76dc-a9d3-52450d061385", "f0a8cd70-1e34-cbe0-04cf-f330edbf8de6"];
 
 lastRows = llStringLength(characters) * llStringLength(characters);
 lastRows -= ((llGetListLength(images) - 1) * columns * rows);
 lastRows = llCeil(lastRows / (float)columns);
 
 width = 1.0 / columns;
 innerWidth = width * 0.8;
 height = 1.0 / rows;
 lastHeight = 1.0 / lastRows;
 
 hOffset = -0.5 + (width * 0.5);
 vOffset = -0.5 - (height * 0.5);
 lastVOffset = -0.5 - (lastHeight * .5);
 
 lastSet = llGetListLength(images) - 1;
 
 list coords = llParseString2List(llGetObjectName(), [";"], []);
 globalRow = llList2Integer(coords, 0);
 globalColumn = llList2Integer(coords, 1);
 showText(message);
}
default
{
 state_entry()
 {
 init();
 } 
 on_rez(integer start_param)
 {
 init();
 }
 link_message(integer sender, integer num, string msg, key id)
 {
 if(num != globalRow) return;
 list params = llParseString2List(msg, [";"], []);
 string command = llList2String(params, 0);
 if(command == "text")
 {
 if(llStringLength(msg) == 5) msg = ""; 
 else msg = llGetSubString(msg, 5, -1);
 while(llStringLength(msg) < 30) msg += " ";
 msg = llGetSubString(msg, globalColumn * 10, (globalColumn * 10) + 9);
 showText(msg);
 }
 else if(command == "color")
 {
 string arg1 = llList2String(params, 1);
 llSetColor((vector)arg1, ALL_SIDES);
 }
 }
}