Difference between revisions of "XyText-UTF8"

From Second Life Wiki
Jump to navigation Jump to search
Line 543: Line 543:
== An example real application in world ==
== An example real application in world ==


** pointer to chalkboard to be written
get or have a look to this opensourced, full permissions and free object on xstreetsl https://www.xstreetsl.com/modules.php?name=Marketplace&file=item&ItemID=1502539 to have some impressions on how this supercell xytext utf8 can be used.

Revision as of 15:57, 28 May 2009

Scripting tools to allow display of text on a prim: XyText 1.5 , XyzzyText, XyyyyzText, XyText-UTF8, XyzzyText-UTF8, ZZText

Introduction

After some years of using XyzzyText I eventually got convinced that Xyzzy is really slow compared to xytext solutions. Probably a bit less lag, but when used in lessons people clearly noticed the filling up of lines terribly slow. So I backported my utf8 experience to XyText.

Also I noticed that whenever I needed to adapt xyXXXX to a new character set or encoding or if wanting to change the parameters for slicing, gridding, offsetting and finding the UTF8 counterpart in the internal lists was really difficult.

This is why here I'm proposing new parametrized CELL using 5 chars able to be driven by external main script.

A primer on UTF-8 encoding (for our needs with lsl and xytext handling)

Just a quick view of the UTF-8 encoding used by SecondLife. It is a very complete and smart way of representing special characters.

With normal characters such as 'A', UTF-8 is perfectly equivalent to ASCII encoding and both will use 1 byte (hexadecimal 41). For special or International characters, then UTF-8 is offering a unique translation which can be more than one byte.

For instance if you take the char 'ç', this will translate (according to http://www.isthisthingon.org/unicode/index.php) in utf8 to: C3A7 (two bytes).

You can also know this writing the following simple lsl script:

<lsl> default {

   touch_start(integer total_number)
   {
       llSay(0, llEscapeURL("ç"));
   }

}

</lsl> which is giving in chat

[15:20]  Object: %C3%A7

Defining the list of UTF8 characters to render and producing with GIMP

I used the following trick to extend standard XyText font texture to accomodate new characters:

  • Put 20 rows of characters instead of the 10 of xytext (we use a 512x1024 texture so we have the SAME quality as of XyText fonts!!!

This allows for 100 more characters added to the usual 100 base characters.

So if you want to adapt this XyText-UTF8 engine to some other language you MUST find the UTF8 encoding for 100 chars you want to use and set up a python fu generating the texture.

The extra 100 characters are defined by a vector of up to 100 elements encoded with UTF8 coding. I use the following python-fu script for gimp to produce a compatible texture for my boards: (you have to put it under .gimp/plug-ins and obviously enable python-fu for your gim.. This tested with gimp 2.4 and gimp 2.6).

This script will appear under Filters->XyText1CharE and when pressed you are asked for

  • font to use (use a non proportional font like Monospace Bold Italic, which I feel it gives one of the best results)
  • Color to use leave white default
  • Font Size (best result with 42 points)
  • limit font generation not used

This sample produces Portuguese fonts. Note the use of a decode array, which mimics the same list used by subsequent lsl scripts in secondlife.

It will show you the image with transparent background which you can save in .tga format and upload to secondlife (or opensim),

<lsl>

  1. ! /usr/bin/env python

from gimpfu import *

def python_log_init():

   fileHandle = open( 'python.log', 'w')
   fileHandle.close()
   

def python_log(s):

   fileHandle = open ( 'python.log', 'a' )
   fileHandle.write(str(s)+"\n")
   fileHandle.close() 

def python_xytext(font,color,size,limit):

 """Print the arguments on standard output"""
 python_log_init()
 python_log("font: %s color: <%d,%d,%d> size: %d limit: %d" % ( font, color[0], color[1], color[2], size, limit ))
 chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~     "
 
 
 decode=  ["\xC3\x87", "\xC3\xBC", "\xC3\xA9", "\xC3\xA2", "\xC3\xA4", "\xC3\xA0", "\xC3\xA5", "\xC3\xA7", "\xC3\xAA", "\xC3\xAB" ]  
 decode+=["\xC3\xA8", "\xC3\xAF", "\xC3\xAE", "\xC3\xAC", "\xC3\x84", "\xC3\x85", "\xC3\x89", "\xC3\xA6", "\xC3\xAE", "\xE2\x96\xB6" ]
 decode+=["\xC3\xB6", "\xC3\xB2", "\xC3\xBB", "\xC3\xB9", "\xC3\xBF", "\xC3\x96", "\xC3\x9C", "\xC2\xA2", "\xC2\xA3", "\xC2\xA5"]
 decode+=["\xC3\x82", "\xC2\xA9", "\xC3\xA1", "\xC3\xAD", "\xC3\xB3", "\xC3\xBA", "\xC3\xB1", "\xC3\x91", "\xC2\xAA", "\xC2\xBA"]
 decode+=["\xC2\xBF", "\xC3\x94", "\xC2\xAC", "\xC2\xBD", "\xC2\xBC", "\xC2\xA1", "\xC2\xAB", "\xC2\xBB", "\xCE\xB1", "\xC3\x9F"]
 decode+=["\xCE\x93", "\xCF\x80", "\xCE\xA3", "\xCF\x83", "\xC2\xB5", "\xCF\x84", "\xCE\xA6", "\xCE\x98", "\xCE\xA9", "\xCE\xB4"]
 decode+=["\xC5\xAC", "\xC5\xAD", "\xCE\xB5", "\xE2\x88\xA9", "\xE2\x89\xA1", "\xC2\xB1", "\xE2\x89\xA5", "\xE2\x89\xA4", " ", " "]
  
 decode+=["\xC3\x81", "\xC3\x80", "\xC3\xA3", "\xC3\x83", "\xC3\x8A", "\xC3\x8D", "\xC3\x93", "\xC3\xB5", "\xC3\x95", "\xC3\xB4" ];

 
 width=512
 height=1024
 img = gimp.Image(width, height, RGB)
 layer = gimp.Layer(img, "my font", width, height, RGB_IMAGE, 100, NORMAL_MODE)
 img.add_layer(layer, 0)
 layer.add_alpha()
 gimp.set_foreground(color)
 pdb.gimp_selection_all(img)
 pdb.gimp_edit_clear(layer)
 pdb.gimp_selection_none(img)
 
 index=0
 extra=-1
 #pdb.gimp_text_fontname(img,layer,50,50,"\xC3\x87",0,TRUE,size,PIXELS,font)
 try:
   for row in range(20):
      for col in range(10):
         if extra<0:
            try:
               el=chars[index]
               index=index+1
            except:
               extra=0
         
         if extra>-1:
            el=decode[extra]
            extra=extra+1
         
            
         python_log(str(row)+","+str(col)+":  "+el)
                
         y=col*51.2+12
         x=row*51.2+5
         pdb.gimp_text_fontname(img,layer,y,x,el,0,TRUE,size,PIXELS,font)
         
 except:
    pass
 
 # Now ready to display this image
 img.merge_visible_layers(0)
 gimp.Display(img)
 
   

register(

 "xytext", "", "", "", "", "",
 "<Toolbox>/Xtns/_XyText1CharE", "",
 [
 (PF_FONT, "font", "Font to use", "Arial"),
 (PF_COLOR,"color","Color to use", (255,255,255) ),
 (PF_INT,    "size", "Font size", 45          ),
 (PF_INT,  "limit", "limit font generation ", 180          ),

 ],
 [],
 python_xytext
 )

main() </lsl>

here the image produced: Portuguese-xytextutf8.jpg

The super parametrizable cell

This cell has the following interesting characteristics:

  • It names itself after the object name number ("1000" for instance) this can be a bit unconvenient, but is a compromise among the linking order of standard xytext and the xyzzytext-m-n. You can find a way to rename all the linked object automagically ;)

Setting parameters and texture

  • It can receive in one shot the configuration of the texture and all parameters:

<lsl>

   llMessageLinked(LINK_SET, 204005, 
               llList2CSV( [  
                   10, // knum number of characters per row
                   <-0.44,0.1,0>, // offset multiplier for cols
                   <0.422,-0.05,0>, // offset multiplier for rows
                   
                   // change the first two in each row to change multiplier for zooming chars
                   <0.15, 0.05, 0>,
                   <0.06, 0.05, 0>, 
                   <-0.86, 0.05, 0>,
                   <0.06, 0.05, 0>, 
                   <0.15, 0.05, 0>, 
                   
                   // change the first two x and y in each row to "move characters along x,y axis
                   <0.035, 0, 0>,
                   <-0.012,0,0>,
                   <-0.298, 0, 0>,
                   <-0.012,0,0>,
                   <-0.055, 0, 0>
                  ]), "db6664f6-057f-becf-bb1e-924255c2c7fd");


</lsl>

Setting the UTF8 decode table

  • It can be instructed on the listing of decode table to understand with a simple message like this:

<lsl> decode = [ "%C3%A8", "%C2%A1", ..... ]; llMessageLinked(LINK_SET, 204006, llList2CSV(decode),NULL_KEY); </lsl> for instance a complete settings for portuguese can be:

Note that this list MUST match that used producing the texture in python-fu as described before!!

<lsl>

            // special UTF-8 chars for European languages
            // these 80 chars correspond to the following chars in CP850 codepage: (some are not viewable in editor)
           
            //           Ç          ü         é         â         ä         à        å         ç          ê        ë
            decode= [ "%C3%87", "%C3%BC", "%C3%A9", "%C3%A2", "%C3%A4", "%C3%A0", "%C3%A5", "%C3%A7", "%C3%AA", "%C3%AB",
            //           è         ï          î         ì         Ä         Å         É         æ         Æ    BLACK RIGHT-POINTING TRIANGLE
                      "%C3%A8", "%C3%AF", "%C3%AE", "%C3%AC", "%C3%84", "%C3%85", "%C3%89", "%C3%A6", "%C3%AE", "%E2%96%B6",
            //           ö         ò          û         ù         ÿ         Ö         Ü         ¢         £         ¥
                      "%C3%B6", "%C3%B2", "%C3%BB", "%C3%B9", "%C3%BF", "%C3%96", "%C3%9C", "%C2%A2", "%C2%A3", "%C2%A5",
            //           A^    copyright     á         í         ó         ú         ñ         Ñ         ª          º"
                      "%C3%82", "%C2%A9", "%C3%A1", "%C3%AD", "%C3%B3", "%C3%BA", "%C3%B1", "%C3%91", "%C2%AA", "%C2%BA",
            //            ¿        O^         ¬        ½         ¼          ¡         «        »       alfa          ß"
                      "%C2%BF", "%C3%94", "%C2%AC", "%C2%BD", "%C2%BC", "%C2%A1", "%C2%AB", "%C2%BB", "%CE%B1", "%C3%9F",
            //           gamma      pi      bigsigma  smsigma    mu  
                      "%CE%93", "%CF%80", "%CE%A3", "%CF%83", "%C2%B5" ];
                     
            //                              tau       BIGfi      theta       omega   delta  
            decode=(decode=[])+decode+ [ "%CF%84", "%CE%A6", "%CE%98", "%CE%A9", "%CE%B4",
            //         Uu          uu       EPS         INTERS        3bars        +-         <=           >=        INTEGRUP   INTEGRDOWN
                      "%C5%AC", "%C5%AD", "%CE%B5", "%E2%88%A9", "%E2%89%A1", "%C2%B1", "%E2%89%A5", "%E2%89%A4", "%E2%8C%A0", "%E2%8C%A1",
            //           A/       A\        a~         A~       E^         I/        O/        o~       O~        o^
                      "%C3%81", "%C3%80", "%C3%A3", "%C3%83", "%C3%8A", "%C3%8D", "%C3%93", "%C3%B5", "%C3%95", "%C3%B4" ];

</lsl>

The cell code

<lsl> //////////////////////////////////////////// // XyText v1.2 Script (5 Face, Single Texture) // Simplified Esperanto version // // Written by Xylor Baysklef // // Modified by Kermitt Quirk 19/01/2006 // To add support for 5 face prim instead of 3 // // Modified by Salahzar Stenvaag to show International UTF8 chars // May 2009 // With easily personalization for each language // ////////////////////////////////////////////

/////////////// CONSTANTS /////////////////// // XyText Message Map. integer SET_PARAMS = 204005; // set params is a CSV value including the following: // # is 1..5 knum, kcol<v>, krow<v>, kmul#<v*5>, koff#<v*5>, and in id=gFontTexture integer SET_DECODE = 204006; integer SET_COLOR = 204007;

// This is an extended character escape sequence. string ESCAPE_SEQUENCE = "\\e";

// This is used to get an index for the extended character. string EXTENDED_INDEX = "12345";

// Face numbers. integer FACE_1 = 3; integer FACE_2 = 7; integer FACE_3 = 4; integer FACE_4 = 6; integer FACE_5 = 1;


// number of characters for each "line" in texture integer knum=10;

// adjusting offset + multipliers for columns and rows vector kcol=<-0.445,0.1,0>; // offset + multiplier for cols vector krow=<0.422,-0.05,0>; // offset + multiplier for rows


// texture resizing for each face vector kmul1=<0.15, 0.05, 0>; // multiplicator for 1st char vector kmul2=<0.06, 0.05, 0>; // multiplicator for 2nd char vector kmul3=<-0.86, 0.05, 0>; // central char vector kmul4=<0.06, 0.05, 0>; vector kmul5=<0.15, 0.05, 0>;

// offset for each face vector koff1=<0.045, 0, 0>; vector koff2=<0,0,0>; vector koff3=<-0.284, 0, 0>; vector koff4=<0,0,0>; vector koff5=<-0.046, 0, 0>;


list decode; integer ME; // Used to hide the text after a fade-out. key TRANSPARENT = "701917a8-d614-471f-13dd-5f4644e36e3c"; ///////////// END CONSTANTS ////////////////

///////////// GLOBAL VARIABLES /////////////// // This is the key of the font we are displaying. key gFontTexture = "ce614b28-e80e-5731-3aca-c23dca1b3b70"; // All displayable characters. Default to ASCII order. string gCharIndex; // This is the channel to listen on while acting // as a cell in a larger display. integer gCellChannel = -1; // This is the starting character position in the cell channel message // to render. integer gCellCharPosition = 0; // This is whether or not to use the fade in/out special effect. integer gCellUseFading = FALSE; // This is how long to display the text before fading out (if using // fading special effect). // Note: < 0 means don't fade out. float gCellHoldDelay = 1.0; /////////// END GLOBAL VARIABLES ////////////

ResetCharIndex() { ’

    gCharIndex  = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`";
    gCharIndex += "abcdefghijklmnopqrstuvwxyz{|}~\n\n\n\n\n";
    decode=[];
    

}

vector GetGridOffset(integer index) {

  // Calculate the offset needed to display this character.
  integer Row = index / knum - 1;
  integer Col = index % knum;
  // Return the offset in the texture.
  return < kcol.x + kcol.y * Col, krow.x + krow.y * Row, 0.0>;

} ShowChars(vector grid_offset1, vector grid_offset2, vector grid_offset3, vector grid_offset4, vector grid_offset5) {

  // Set the primitive textures directly.
  
  llSetPrimitiveParams( [
       PRIM_TEXTURE, FACE_1, (string)gFontTexture, kmul1, grid_offset1 + koff1, 0.0,
       PRIM_TEXTURE, FACE_2, (string)gFontTexture, kmul2, grid_offset2 + koff2, 0.0,
       PRIM_TEXTURE, FACE_3, (string)gFontTexture, kmul3, grid_offset3 + koff3, 0.0,
       PRIM_TEXTURE, FACE_4, (string)gFontTexture, kmul4, grid_offset4 + koff4, 0.0,
       PRIM_TEXTURE, FACE_5, (string)gFontTexture, kmul5, grid_offset5 + koff5, 0.0
       ]);

}

// SALAHZAR intelligent procedure to extract UTF-8 codes and convert to index in our "cp850"-like table integer GetIndex(string char) {

   integer  ret=llSubStringIndex(gCharIndex, char);
   //llSay(0,"pos: "+(string)ret);
   if(ret>=0) return ret;

   // special char do nice trick :)
   string escaped=llEscapeURL(char);
   //llSay(0,"char: "+escaped);
   integer found=llListFindList(decode, [escaped]);

   // Return blank if not found
   if(found<0) return 0;

   // return correct index
   return 100+found;

} // END SALAHZAR


RenderString(string str) {

  // Get the grid positions for each pair of characters. 
  vector GridOffset1 = GetGridOffset( GetIndex(llGetSubString(str, 0, 0)) ); // SALAHZAR intermediate function
  vector GridOffset2 = GetGridOffset( GetIndex(llGetSubString(str, 1, 1)) ); // SALAHZAR
  vector GridOffset3 = GetGridOffset( GetIndex(llGetSubString(str, 2, 2)) ); // SALAHZAR
  vector GridOffset4 = GetGridOffset( GetIndex(llGetSubString(str, 3, 3)) ); // SALAHZAR
  vector GridOffset5 = GetGridOffset( GetIndex(llGetSubString(str, 4, 4)) ); // SALAHZAR

  // Use these grid positions to display the correct textures/offsets. 
  ShowChars(GridOffset1, GridOffset2, GridOffset3, GridOffset4, GridOffset5); 

}

integer ConvertIndex(integer index) {

  // This converts from an ASCII based index to our indexing scheme.
  if (index >= 32) // ' ' or higher
      index -= 32;
  else { // index < 32
      // Quick bounds check.
      if (index > 15)
          index = 15;
      index += 94; // extended characters
  }
  return index;

}

default {

  state_entry() {
      // Initialize the character index.
      ResetCharIndex(); ME=(integer)llGetObjectName();
      RenderString(llGetObjectName());
      
  }
  link_message(integer sender, integer channel, string data, key id) {
      if (channel == ME) {
          RenderString(data);
          return;
      }
      // set_params will return in key the texture id, and in data
      // knum, kcol<v>, krow<v>, kmul#<v*5>, koff#<v*5>, and in id=gFontTexture
      if (channel == SET_PARAMS) {
          llSetText("*",<1,1,1>,1); // show we are working
          list l=llCSV2List(data);
          // Use the new texture instead of the current one.
          gFontTexture = id;
          // Change the currently shown texture.
          llSetTexture(gFontTexture, FACE_1);
          llSetTexture(gFontTexture, FACE_2);
          llSetTexture(gFontTexture, FACE_3);
          llSetTexture(gFontTexture, FACE_4);
          llSetTexture(gFontTexture, FACE_5);
          integer i=0;
          knum=(integer)llList2String(l,i++);
          kcol=(vector)llList2String(l,i++);
          krow=(vector)llList2String(l,i++);
          // kmul#
          kmul1=(vector)llList2String(l,i++);
          kmul2=(vector)llList2String(l,i++);
          kmul3=(vector)llList2String(l,i++);
          kmul4=(vector)llList2String(l,i++);
          kmul5=(vector)llList2String(l,i++);
          // koff#
          koff1=(vector)llList2String(l,i++);
          koff2=(vector)llList2String(l,i++);
          koff3=(vector)llList2String(l,i++);
          koff4=(vector)llList2String(l,i++);
          koff5=(vector)llList2String(l,i++);
          
          
          llSetText("",<1,1,1>,1); // remove working mark
          //RenderString("mmmmm");
          
          return;
      }
      // change the decode array to be able to easily change font
      if(channel == SET_DECODE) {
          
          // we are lucky since decode does NOT contain normal characters like ","
          decode=llCSV2List(data);
          return;
      }
      if (channel == SET_COLOR) {
          vector newColor = (vector)data;
          llSetColor(newColor, ALL_SIDES);
      }
  }

} </lsl>

A very simple application

<lsl> // // This is a sample for driving the XyText SuperCell // setCells() {

   // set positions and texture
   
   llMessageLinked(LINK_SET, 204005, 
       llList2CSV( [  
           10, // knum number of characters per row
           <-0.44,0.1,0>, // offset multiplier for cols
           <0.422,-0.05,0>, // offset multiplier for rows
           // change the first two in each row to change multiplier for zooming chars
           <0.15, 0.05, 0>,
           <0.06, 0.05, 0>, 
           <-0.86, 0.05, 0>,
           <0.06, 0.05, 0>, 
           <0.15, 0.05, 0>, 
           // change the first two x and y in each row to "move characters along x,y axis
           <0.035, 0, 0>,
           <-0.012,0,0>,
           <-0.298, 0, 0>,
           <-0.012,0,0>,
           <-0.055, 0, 0>
          ]), "db6664f6-057f-becf-bb1e-924255c2c7fd");
   
       
   // special UTF-8 chars for European languages
   // these 80 chars correspond to the following chars in CP850 codepage: (some are not viewable in editor)

            //           Ç          ü         é         â         ä         à        å         ç          ê        ë
       list decode= [ "%C3%87", "%C3%BC", "%C3%A9", "%C3%A2", "%C3%A4", "%C3%A0", "%C3%A5", "%C3%A7", "%C3%AA", "%C3%AB",
            //           è         ï          î         ì         Ä         Å         É         æ         Æ    BLACK RIGHT-POINTING TRIANGLE
                      "%C3%A8", "%C3%AF", "%C3%AE", "%C3%AC", "%C3%84", "%C3%85", "%C3%89", "%C3%A6", "%C3%AE", "%E2%96%B6",
            //           ö         ò          û         ù         ÿ         Ö         Ü         ¢         £         ¥
                      "%C3%B6", "%C3%B2", "%C3%BB", "%C3%B9", "%C3%BF", "%C3%96", "%C3%9C", "%C2%A2", "%C2%A3", "%C2%A5",
            //           A^    copyright     á         í         ó         ú         ñ         Ñ         ª          º"
                      "%C3%82", "%C2%A9", "%C3%A1", "%C3%AD", "%C3%B3", "%C3%BA", "%C3%B1", "%C3%91", "%C2%AA", "%C2%BA",
            //            ¿        O^         ¬        ½         ¼          ¡         «        »       alfa          ß"
                      "%C2%BF", "%C3%94", "%C2%AC", "%C2%BD", "%C2%BC", "%C2%A1", "%C2%AB", "%C2%BB", "%CE%B1", "%C3%9F",
            //           gamma      pi      bigsigma  smsigma    mu  
                      "%CE%93", "%CF%80", "%CE%A3", "%CF%83", "%C2%B5" ];

            //                              tau       BIGfi      theta       omega   delta  
            decode=(decode=[])+decode+ [ "%CF%84", "%CE%A6", "%CE%98", "%CE%A9", "%CE%B4",
            //         Uu          uu       EPS         INTERS        3bars        +-         <=           >=        INTEGRUP   INTEGRDOWN
                      "%C5%AC", "%C5%AD", "%CE%B5", "%E2%88%A9", "%E2%89%A1", "%C2%B1", "%E2%89%A5", "%E2%89%A4", "%E2%8C%A0", "%E2%8C%A1",
            //           A/       A\        a~         A~       E^         I/        O/        o~       O~        o^
                      "%C3%81", "%C3%80", "%C3%A3", "%C3%83", "%C3%8A", "%C3%8D", "%C3%93", "%C3%B5", "%C3%95", "%C3%B4" ];

           llMessageLinked(LINK_SET, 204006, llList2CSV(decode),NULL_KEY);

}


default {

   state_entry()
   {
       // set properly the supercells
       setCells();
       
       // clear cells 1000 1002
       llMessageLinked(LINK_SET,1000," "," ");
       llMessageLinked(LINK_SET,1001," "," ");
       llMessageLinked(LINK_SET,1002," "," ");
       llMessageLinked(LINK_SET,1003," "," ");
       
       
       
   }
   touch_start(integer total_number)
   {
       string avatarName=llDetectedName(0);
       llMessageLinked(LINK_SET,1000,llGetSubString(avatarName,0,4),"");
       llMessageLinked(LINK_SET,1001,llGetSubString(avatarName,5,9),"");
       llMessageLinked(LINK_SET,1002,llGetSubString(avatarName,10,14),"");
       llMessageLinked(LINK_SET,1002,llGetSubString(avatarName,15,19),"");
       
   }

}

</lsl>

An example real application in world

get or have a look to this opensourced, full permissions and free object on xstreetsl https://www.xstreetsl.com/modules.php?name=Marketplace&file=item&ItemID=1502539 to have some impressions on how this supercell xytext utf8 can be used.