Difference between revisions of "LCL Unicode Support/es"

From Free Pascal wiki
Jump to navigationJump to search
m (Fixed syntax highlighting)
 
(17 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{LCL Unicode Support}}[[category:Castellano]][[category:Español]]
+
{{LCL Unicode Support}}[[category:Castellano]][[category:Español]][[Category:LCL/es]]
  
 
== Introducción ==  
 
== Introducción ==  
  
En la versión 0.9.25, Lazarus tiene soporte completo para Unicode en todas las plataformas
+
   En la versión 0.9.25, Lazarus tiene soporte completo para Unicode en todas las plataformas a excepción de Gtk 1. En esta página se pueden encontrar instrucciones para usuarios de Lazarus, roadmaps, descripción de conceptos básicos y detalles de implementación.
a excepción de Gtk 1. En esta página se pueden encontrar instrucciones para usuarios de La-
 
zarus, roadmaps, descripción de conceptos básicos y detalles de implementación.
 
  
 
== Instrucciones para usuarios ==
 
== Instrucciones para usuarios ==
  
En los widgetsets unicode es importante hacer ver que no todo es unicode. La librerias
+
   En los widgetsets unicode es importante hacer ver que no todo es Unicode. Las librerías Runtime Free Pascal y FCL son ANSI. Es responsabilidad del desarrollador conocer cual es la codificación de sus cadenas y realizar la conversión apropiada entre librerías que esperan diferentes codificaciones.  
Free Runtime Pascal y FCL son ANSI. Es responsabilidad del desarrollador conocer cual es
 
la codificación de sus cadenas y realizar la conversión apropiada entre librerias que es-
 
peran diferentes codificaciones.  
 
  
Usualmente la codificación es por libreria. Cada librería esperará uniformemente un tipo de codificación, que será habitualmente o bien UNICODE (UTF-8 para Lazarus) o ANSI (lo cual significa que es la codificación del sistema, que puede ser UTF-8 o no). Tanto RTL como FCL de Free Pascal Compiler (FPC) 2.2.2 esperan cadenas ANSI (también FPC 2.3.x).
+
   Usualmente la codificación es por librería. Cada librería esperará uniformemente un tipo de codificación, que será habitualmente o bien UNICODE (UTF-8 para Lazarus) o ANSI (lo cual significa que es la codificación del sistema, que puede ser UTF-8 o no). Tanto RTL como FCL de Free Pascal Compiler (FPC) 2.2.2 esperan cadenas ANSI (también FPC 2.3.x).
  
Se puede realizar la conversión entre UNICODE y ANSI utilizando las funciones UTF8ToAnsi y AnsiToUTF8 de la unit System o bien las funciones UTF8ToSys y SysToUTF8 de la unidad FileUtil. Las dos últimas son más elegantes pero insertan más código en el programa.
+
   Se puede realizar la conversión entre UNICODE y ANSI utilizando las funciones UTF8ToAnsi y AnsiToUTF8 de la unit System o bien las funciones UTF8ToSys y SysToUTF8 de la unidad FileUtil. Las dos últimas son más elegantes pero insertan más código en el programa.
  
Ejemplos:
+
   Ejemplos:
  
Digamos que tienes una cadena desde un Tedit y necesitas pasársela a alguna rutina de fichero rtl:
+
   Digamos que tienes una cadena desde un TEdit y necesitas pasársela a alguna rutina de fichero rtl:
  
<delphi>
+
<syntaxhighlight lang=pascal> var
var
+
  miCadena: string; // codificación utf-8  
  MyString: string; // codificación utf-8  
+
begin
begin
+
  miCadena := miTEdit.Text;
  MyString := MyTEdit.Text;
+
  unaRutinaRTL(UTF8ToAnsi(miCadena));
  SomeRTLRoutine(UTF8ToAnsi(MyString));
+
end;</syntaxhighlight>
end;
+
 
</delphi>
+
&nbsp;&nbsp;&nbsp;Y en el modo inverso:
 +
 
 +
<syntaxhighlight lang=pascal> var
 +
  miCadena: string; // codificada ansi
 +
begin
 +
  miCadena := unaRutinaRTL;
 +
  miTEdit.Text := AnsiToUTF8(miCadena);
 +
end;</syntaxhighlight>
 +
 
 +
&nbsp;&nbsp;&nbsp;'''Importante''': UTF8ToAnsi retornará una cadena vacía si la cadena UTF8 contiene caracteres no válidos.
 +
 
 +
&nbsp;&nbsp;&nbsp;'''Importante''': AnsiToUTF8 y UTF8ToAnsi requiere un gestor de cadenas largas bajo Linux, BSD y Mac OS X. Puedes utilizar las funciones SysToUTF8 y UTF8ToSys (unidad FileUtil) o añadir un gestor de cadenas largas mediante cwstring como unidad en la sección ''uses''.
 +
 
 +
===Tratamiento de cadenas UTF-8 y de caracteres===
 +
 
 +
&nbsp;&nbsp;&nbsp;Si deseamos actuar sobre los caracteres de una cadena UTF8, básicamente hay dos maneras.
 +
 
 +
* actuar sobre los bytes - útil para buscar una subcadena o cuando se busca sólo en los caracteres ASCII de la cadena UTF8. Por ejemplo, al analizar archivos xml.
 +
* actuar a través de los caracteres - útil para componentes gráficos como SynEdit. Por ejemplo cuando se desea conocer el tercer carácter que aparece en la pantalla.
 +
 
 +
====Buscando una subcadena====
  
Y en el modo inverso:
+
&nbsp;&nbsp;&nbsp;Debido a la naturaleza especial de UTF8 podemos simplemente usar las funciones normales de cadena:
  
<delphi>
+
<syntaxhighlight lang=pascal> procedure Where(SearchFor, aText: string);
var
+
var
   MyString: string; // ansi encoded
+
   BytePos: LongInt;
begin
+
  CharacterPos: LongInt;
   MyString := SomeRTLRoutine;
+
begin
   MyTEdit.Text := AnsiToUTF8(MyString);
+
   BytePos:=Pos(SearchFor,aText);
end;
+
   CharacterPos:=UTF8Length(PChar(aText),BytePos-1);
</delphi>
+
  writeln('La subcadena "',SearchFor,'" está en el texto "',aText,'"',
 +
    ' en la posición de byte ',BytePos,' y en la posición de caracter ',CharacterPos);
 +
end;</syntaxhighlight>
  
'''Importante''': UTF8ToAnsi retornará una cadena vacía si la cadena UTF8 contiene caracteres no válidos.
+
====Accediendo a los caracteres UTF8====
  
'''Importante''': AnsiToUTF8 y UTF8ToAnsi requiere un gestor de cadenas largas bajo Linux, BSD y Mac OS X. Puedes utilizar las funciones SysToUTF8 y UTF8ToSys (unit FileUtil) o añadir un gestor de cadenas largas mediante cwstring como unit en la sección uses.
+
&nbsp;&nbsp;&nbsp;Los caracteres Unicode pueden variar en longitud, por lo que la mejor solución para acceder a ellos es usar una iteración cuando queremos acceder a los caracteres en la secuencia en la que se encuentran. Para ello podemos utilizar este código:
  
===Tratando con directorios y nombres de fichero===
+
<syntaxhighlight lang=pascal> uses lazutf8; // LCLProc para Lazarus 0.9.30 o inferior
 +
...
 +
procedure DoSomethingWithString(AnUTF8String: string);
 +
var
 +
  p: PChar;
 +
  CharLen: integer;
 +
  FirstByte, SecondByte, ThirdByte: Char;
 +
begin
 +
  p:=PChar(AnUTF8String);
 +
  repeat
 +
    CharLen := UTF8CharacterLength(p);
  
Las funciones y controles de Lazarus esperan nombres de directorios y ficheros con la codificación utf-8, pero RTL utiliza cadenas ANSI para los mismos.
+
    // Ahora tenemos un puntero al caracter y su longitud
 +
    // Podemos acceder a los bytes del caracter UTF-8 así:
 +
    if CharLen >= 1 then FirstByte := P[0];
 +
    if CharLen >= 2 then SecondByte := P[1];
 +
    if CharLen >= 3 then ThirdByte := P[2];
  
For ejemplo, consideremos un pulsador (button), el cual establece la propiedad directorio de TFileListBox al valor del directorio actual. La función RTL [[doc:rtl/sysutils/getcurrentdir.html|GetCurrentDir]] es ANSI, y no UNICODE, lo cual precisa de una conversión:
+
    inc(p,CharLen);
 +
  until (CharLen=0) or (p^ = #0);
 +
end;</syntaxhighlight>
  
<delphi>
+
====Acceder al enésimo caracter UTF8====
procedure TForm1.Button1Click(Sender: TObject);
 
begin
 
  FileListBox1.Directory:=SysToUTF8(GetCurrentDir);
 
  // o usar las funciones de la unit FileUtil
 
  FileListBox1.Directory:=GetCurrentDirUTF8;
 
end;
 
</delphi>
 
  
La unit FileUtil define funciones comunes de fichero con cadenas UTF-8:
+
&nbsp;&nbsp;&nbsp;Además de la iteración, podemos querer tambien realizar un acceso aleatorio a los caracteres UTF-8.
  
<Delphi>
+
<syntaxhighlight lang=pascal> uses lazutf8; // LCLProc para Lazarus 0.9.30 o inferior
// funciones basicas similares a la RTL pero trabajando con UTF-8 en lugar de
+
...
// codificación de sistema
+
var
 +
  AnUTF8String, NthChar: string;
 +
begin
 +
  NthChar := UTF8Copy(AnUTF8String, N, 1);
 +
</syntaxhighlight>
  
// AnsiToUTF8 y UTF8ToAnsi necesitan un gestor de cadenas largas (widestring) bajo Linux, BSD, MacOSX
+
====Iterar sobre los puntos de código usando UTF8CharacterToUnicode====
// pero normalmente estos sistemas operativos utilizan UTF-8 como sistema de codificación
 
// por lo que el gestor de cadenas largas no es necesario.
 
  
function NeedRTLAnsi: boolean;// true si la codificación no es UTF-8
+
&nbsp;&nbsp;&nbsp;A continuación se muestra cómo recorrer en iteración el valor del código de 32 bits de punto para cada carácter de una cadena UTF8:
procedure SetNeedRTLAnsi(NewValue: boolean);
 
function UTF8ToSys(const s: string): string;// como UTF8ToAnsi pero más independiente de widestringmanager
 
function SysToUTF8(const s: string): string;// como AnsiToUTF8 pero mas independiente de widestringmanager
 
  
// operaciones de fichero
+
<syntaxhighlight lang=pascal> uses lazutf8; // LCLProc para Lazarus 0.9.30 o inferior
function FileExistsUTF8(const Filename: string): boolean;
+
...
function FileAgeUTF8(const FileName: string): Longint;
+
procedure IterateUTF8Characters(const AnUTF8String: string);
function DirectoryExistsUTF8(const Directory: string): Boolean;
+
var
function ExpandFileNameUTF8(const FileName: string): string;
+
  p: PChar;
function ExpandUNCFileNameUTF8(const FileName: string): string;
+
  unicode: Cardinal;
{$IFNDEF VER2_2_0}
+
  CharLen: integer;
function ExtractShortPathNameUTF8(Const FileName : String) : String;
+
begin
{$ENDIF}
+
  p:=PChar(AnUTF8String);
function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;
+
  repeat
function FindNextUTF8(var Rslt: TSearchRec): Longint;
+
    unicode:=UTF8CharacterToUnicode(p,CharLen);
procedure FindCloseUTF8(var F: TSearchrec);
+
    writeln('Unicode=',unicode);
function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
+
    inc(p,CharLen);
function FileGetAttrUTF8(const FileName: String): Longint;
+
  until (CharLen=0) or (unicode=0);
function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
+
end;</syntaxhighlight>
function DeleteFileUTF8(const FileName: String): Boolean;
 
function RenameFileUTF8(const OldName, NewName: String): Boolean;
 
function FileSearchUTF8(const Name, DirList : String): String;
 
function FileIsReadOnlyUTF8(const FileName: String): Boolean;
 
function GetCurrentDirUTF8: String;
 
function SetCurrentDirUTF8(const NewDir: String): Boolean;
 
function CreateDirUTF8(const NewDir: String): Boolean;
 
function RemoveDirUTF8(const Dir: String): Boolean;
 
function ForceDirectoriesUTF8(const Dir: string): Boolean;
 
  
// entorno
+
====UTF-8: Copiar cadena, Longitud, Minúsculas, etc====
function ParamStrUTF8(Param: Integer): string;
 
function GetEnvironmentStringUTF8(Index : Integer): String;
 
function GetEnvironmentVariableUTF8(const EnvVar: String): String;
 
function GetAppConfigDirUTF8(Global: Boolean): string;
 
</Delphi>
 
  
Aunque la mayor parte de los parámetros pasados a estas funciones tienen unos tipos más simples otros tienen más complejidad, por eso añado aquí contenido para no tener que ir a buscar a otro sitio:
+
&nbsp;&nbsp;&nbsp;Casi todas las operaciones que podríamos querer ejecutar con cadenas UTF-8 están cubiertas por las rutinas en la unidad de lazutf8 (unidad LCLProc en Lazarus 0.9.30 o inferior). Consulte la siguiente lista de las rutinas de tomada de lazutf8.pas:
Descripción de recursos utilizados:
 
  
<delphi>
+
<syntaxhighlight lang=pascal> function UTF8CharacterLength(p: PChar): integer;
function FindFirstUTF8(
+
function UTF8Length(const s: string): PtrInt;
 +
function UTF8Length(p: PChar; ByteCount: PtrInt): PtrInt;
 +
function UTF8CharacterToUnicode(p: PChar; out CharLen: integer): Cardinal;
 +
function UnicodeToUTF8(u: cardinal; Buf: PChar): integer; inline;
 +
function UnicodeToUTF8SkipErrors(u: cardinal; Buf: PChar): integer;
 +
function UnicodeToUTF8(u: cardinal): shortstring; inline;
 +
function UTF8ToDoubleByteString(const s: string): string;
 +
function UTF8ToDoubleByte(UTF8Str: PChar; Len: PtrInt; DBStr: PByte): PtrInt;
 +
function UTF8FindNearestCharStart(UTF8Str: PChar; Len: integer;
 +
                                  BytePos: integer): integer;
 +
// encontrar el caracter enésimo UTF8, ignorando BIDI
 +
function UTF8CharStart(UTF8Str: PChar; Len, CharIndex: PtrInt): PChar;
 +
// encontrar el ínidice de bytedel enésimo caracter UTF8, ignorando BIDI (longitud en bytes de la subcadena)
 +
function UTF8CharToByteIndex(UTF8Str: PChar; Len, CharIndex: PtrInt): PtrInt;
 +
procedure UTF8FixBroken(P: PChar);
 +
function UTF8CharacterStrictLength(P: PChar): integer;
 +
function UTF8CStringToUTF8String(SourceStart: PChar; SourceLen: PtrInt) : string;
 +
function UTF8Pos(const SearchForText, SearchInText: string): PtrInt;
 +
function UTF8Copy(const s: string; StartCharIndex, CharCount: PtrInt): string;
 +
procedure UTF8Delete(var s: String; StartCharIndex, CharCount: PtrInt);
 +
procedure UTF8Insert(const source: String; var s: string; StartCharIndex: PtrInt);
  
  filtro:string;       // le indicamos el filtro a utilizar por ejemplo *.*, *.txt, *.exe....
+
function UTF8LowerCase(const AInStr: string; ALanguage: string=''): string;
 +
function UTF8UpperCase(const AInStr: string; ALanguage: string=''): string;
 +
function FindInvalidUTF8Character(p: PChar; Count: PtrInt;
 +
                                  StopOnNonASCII: Boolean = false): PtrInt;
 +
function ValidUTF8String(const s: String): String;
  
  Attr: LongInt;       // atributos de búsqueda
+
procedure AssignUTF8ListToAnsi(UTF8List, AnsiList: TStrings);
  
  out Rslt: TSearchRec // devuelve el resultado buscado con respecto a path y Attr
+
  //funciones de comparación
  
):LongInt;            
+
function UTF8CompareStr(const S1, S2: string): Integer;
 +
function UTF8CompareText(const S1, S2: string): Integer;</syntaxhighlight>
  
// Para establecer la trayectoria donde empezar a buscar se lo indicamos con SetCurrentDir()
+
===Tratando con directorios y nombres de fichero===
// por ejemplo SetCurrentDir ('c:\').
 
// Para saber que trayectoria ha establecido realmente se lo podemos preguntar con GetCurrentDir
 
// claro que podemos recurrir al resultado de la llamada para saber si tuvo exito pero de todos
 
// modos definimos una variable string    directorio_actual:string y luego directorio_actual:=GetCurrentDir();
 
//
 
// Para encontrar sucesivas entradas se necesitaria utilizar FindNextUTF8.
 
// Para liberar los recursos utilizados podemos emplear FindCloseUTF8.
 
  
// Attr: LongInt donde los atributos pueden ser los siguientes:
+
&nbsp;&nbsp;&nbsp;Las funciones y controles de Lazarus esperan nombres de directorios y ficheros con la codificación UTF-8, pero RTL utiliza cadenas ANSI para los mismos.
//
 
// faReadOnly = $00000001;
 
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/fareadonly.html
 
// const faHidden = $00000002;
 
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/fahidden.html
 
//
 
// const faSysFile = $00000004;
 
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/fasysfile.html
 
// const faVolumeId = $00000008;
 
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/favolumeid.html
 
// const faDirectory = $00000010;
 
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/fadirectory.html
 
// const faArchive = $00000020;
 
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/faarchive.html
 
// const faAnyFile = $0000003f;
 
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/faanyfile.html
 
// const faSymLink = $00000040;
 
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/fasymlink.html 
 
// Son constantes que se pueden usar tal cual o por su valor numérico (longint)
 
// como puede ser $00000008 para obtener el volumen de la unidad de disco.
 
// He recogido la información basándome en FindFirst no FindFirstUTF si me equivoco
 
// que me corrijan, además para sistemas Linux he encontrado esto:
 
// It is not recommended to use findfirst/findnext on Linux.
 
// It is an (ineffective) emulation. Instead try using the linux unit
 
// and using 'Glob' instead.
 
//
 
// I have plans for a better findfirst/findnext algorithm on Linux,
 
// but I need some time to implement it.
 
//
 
//-- Michael Van Canneyt, 18 octubre 2000 15:10 
 
// De eso ya hace tiempo, yo aún no he podido practicar el equivalente en Linux
 
// pero lo dejo de referencia.
 
</Delphi>
 
  
<delphi>
+
&nbsp;&nbsp;&nbsp;Por ejemplo, consideremos un botón, que establece la propiedad directorio de TFileListBox al valor del directorio actual. La función RTL [[doc:rtl/sysutils/getcurrentdir.html|GetCurrentDir]] es ANSI, y no UNICODE, lo cual precisa de una conversión:
type TSearchRec = record
 
  
  Time: LongInt;  // Timestamp del fichero.
+
<syntaxhighlight lang=pascal> procedure TForm1.Boton1Click(Sender: TObject);
 +
  begin
 +
  FileListBox1.Directory:=SysToUTF8(GetCurrentDir);
 +
  // o usar las funciones de la unidad FileUtil
 +
  FileListBox1.Directory:=GetCurrentDirUTF8;
 +
end;</syntaxhighlight>
  
  Size: Int64;   // Tamaño del fichero.
+
&nbsp;&nbsp;&nbsp;La unidad FileUtil define funciones comunes de fichero con cadenas UTF-8:
  
  Attr: LongInt; // Attributos del fichero.
+
<syntaxhighlight lang=pascal> // funciones básicas similares a la RTL pero trabajando con UTF-8 en lugar de
 +
// codif cación de sistema
 +
// AnsiToUTF8 y UTF8ToAnsi necesitan un gestor de cadenas largas (widestring) bajo Linux, BSD, MacOSX
 +
// pero normalmente estos sistemas operativos utilizan UTF-8 como sistema de codificación
 +
  // por lo que el gestor de cadenas largas no es necesario.
  
  Name: TFilename; // Nombre del fichero (no parte de directorio).
+
function NeedRTLAnsi: boolean;// true si la codificación no es UTF-8
 +
procedure SetNeedRTLAnsi(NewValue: boolean);
 +
function UTF8ToSys(const s: string): string;// como UTF8ToAnsi pero más independiente de widestringmanager
 +
function SysToUTF8(const s: string): string;// como AnsiToUTF8 pero mas independiente de widestringmanager
  
  ExcludeAttr: LongInt; // Attributos a excluir de la búsqueda (no usar).
+
// operaciones de fichero
 +
function FileExistsUTF8(const Filename: string): boolean;
 +
function FileAgeUTF8(const FileName: string): Longint;
 +
function DirectoryExistsUTF8(const Directory: string): Boolean;
 +
function ExpandFileNameUTF8(const FileName: string): string;
 +
function ExpandUNCFileNameUTF8(const FileName: string): string;
 +
{$IFNDEF VER2_2_0}
 +
function ExtractShortPathNameUTF8(Const FileName : String) : String;
 +
{$ENDIF}
 +
function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;
 +
function FindNextUTF8(var Rslt: TSearchRec): Longint;
 +
procedure FindCloseUTF8(var F: TSearchrec);
 +
function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
 +
function FileGetAttrUTF8(const FileName: String): Longint;
 +
function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
 +
function DeleteFileUTF8(const FileName: String): Boolean;
 +
function RenameFileUTF8(const OldName, NewName: String): Boolean;
 +
function FileSearchUTF8(const Name, DirList : String): String;
 +
function FileIsReadOnlyUTF8(const FileName: String): Boolean;
 +
function GetCurrentDirUTF8: String;
 +
function SetCurrentDirUTF8(const NewDir: String): Boolean;
 +
function CreateDirUTF8(const NewDir: String): Boolean;
 +
function RemoveDirUTF8(const Dir: String): Boolean;
 +
function ForceDirectoriesUTF8(const Dir: string): Boolean;
  
  FindHandle: Pointer; // Manejador (handle) interno del sistema operativo(no usar).
+
// entorno
 +
function ParamStrUTF8(Param: Integer): string;
 +
function GetEnvironmentStringUTF8(Index : Integer): String;
 +
function GetEnvironmentVariableUTF8(const EnvVar: String): String;
 +
function GetAppConfigDirUTF8(Global: Boolean): string;</syntaxhighlight>
  
  Mode: TMode;         //Modo de fichero UNIX. Solo usado en sistemas UNIX.
+
&nbsp;&nbsp;&nbsp;Aunque la mayor parte de los parámetros pasados a estas funciones tienen unos tipos más simples otros tienen más complejidad, por eso añado aquí contenido para no tener que ir a buscar a otro sitio:
 +
&nbsp;&nbsp;&nbsp;Descripción de recursos utilizados:
  
  PathOnly: AnsiString; // trayecto de un fichero.
+
<syntaxhighlight lang=pascal> function FindFirstUTF8(
 +
  filtro:string;       // le indicamos el filtro a utilizar por ejemplo *.*, *.txt, *.exe....
 +
  Attr: LongInt;        // atributos de búsqueda
 +
  out Rslt: TSearchRec  // devuelve el resultado buscado con respecto a path y Attr
 +
):LongInt;             
  
end;  
+
// Para establecer la trayectoria donde empezar a buscar se lo indicamos con SetCurrentDir()
 +
// por ejemplo SetCurrentDir ('c:\').
 +
// Para saber que trayectoria ha establecido realmente se lo podemos preguntar con GetCurrentDir
 +
// claro que podemos recurrir al resultado de la llamada para saber si tuvo exito pero de todos
 +
// modos definimos una variable string    directorio_actual:string y luego directorio_actual:=GetCurrentDir();
 +
//
 +
// Para encontrar sucesivas entradas se necesitaría utilizar FindNextUTF8.
 +
// Para liberar los recursos utilizados podemos emplear FindCloseUTF8.
  
// donde type TFilename = String;
+
// Attr: LongInt donde los atributos pueden ser los siguientes:
//  
+
//
</Delphi>
+
// faReadOnly = $00000001;
 +
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/fareadonly.html
 +
// const faHidden = $00000002;
 +
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/fahidden.html
 +
//
 +
// const faSysFile = $00000004;
 +
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/fasysfile.html
 +
// const faVolumeId = $00000008;
 +
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/favolumeid.html
 +
// const faDirectory = $00000010;
 +
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/fadirectory.html
 +
// const faArchive = $00000020;
 +
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/faarchive.html
 +
// const faAnyFile = $0000003f;
 +
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/faanyfile.html
 +
// const faSymLink = $00000040;
 +
// http://community.freepascal.org:10000/docs-html/rtl/sysutils/fasymlink.html 
 +
// Son constantes que se pueden usar tal cual o por su valor numérico (longint)
 +
// como puede ser $00000008 para obtener el volumen de la unidad de disco.
 +
// He recogido la información basándome en FindFirst no FindFirstUTF si me equivoco
 +
// que me corrijan, además para sistemas Linux he encontrado esto:
 +
// It is not recommended to use findfirst/findnext on Linux.
 +
// It is an (ineffective) emulation. Instead try using the linux unit
 +
// and using 'Glob' instead.
 +
//
 +
// I have plans for a better findfirst/findnext algorithm on Linux,
 +
// but I need some time to implement it.
 +
//
 +
//-- Michael Van Canneyt, 18 octubre 2000 15:10 
 +
// De eso ya hace tiempo, yo aún no he podido practicar el equivalente en Linux
 +
// pero lo dejo de referencia.</syntaxhighlight>
  
El siguiente es un pequeño ejemplo aplicado a un Button:
+
<syntaxhighlight lang=pascal> type TSearchRec = record
   Creamos varias etiquetas (label) en las que por medio de su propiedad caption visualizaremos
+
  Time: LongInt;  // Timestamp del fichero.
   los resultados
+
  Size: Int64;    // Tamaño del fichero.
 +
  Attr: LongInt;  // Atributos del fichero.
 +
  Name: TFilename; // Nombre del fichero (no parte de directorio).
 +
  ExcludeAttr: LongInt; // Atributos a excluir de la búsqueda (no usar).
 +
   FindHandle: Pointer; // Manejador (handle) interno del sistema operativo(no usar).
 +
  Mode: TMode;        //Modo de fichero UNIX. Solo usado en sistemas UNIX.
 +
  PathOnly: AnsiString; // trayecto de un fichero.
 +
end;    
 +
// donde type TFilename = String;
 +
//</syntaxhighlight>
  
<Delphi>
+
&nbsp;&nbsp;&nbsp;El siguiente es un pequeño ejemplo aplicado a un botón:
procedure TForm1.Button1Click(Sender: TObject);
+
&nbsp;&nbsp;&nbsp;Creamos varias etiquetas (TLabel) en las que por medio de su propiedad caption visualizaremos los resultados
var
+
 
lista_ficheros:TSearchRec;
+
<syntaxhighlight lang=pascal> procedure TForm1.Button1Click(Sender: TObject);
directorio_actual:string;
+
var
trayecto: string;
+
  lista_ficheros:TSearchRec;
begin
+
  directorio_actual:string;
 +
  trayecto: string;
 +
begin
 
   lista_ficheros.name:='        ';
 
   lista_ficheros.name:='        ';
 
   SetCurrentDir('c:\');
 
   SetCurrentDir('c:\');
Line 219: Line 305:
 
   else
 
   else
 
   label1.caption:='Hay un error en alguna parte';
 
   label1.caption:='Hay un error en alguna parte';
end;
+
end;</syntaxhighlight>
</Delphi>
+
 
 
====Mac OS X====
 
====Mac OS X====
  
Las funciones de fichero de la unit FileUtil tienen especial cuidado en Mac OS X: OS X normaliza los nombres de fichero. Por ejemplo el nombre de fichero 'ä.txt' puede ser codificado en unicode con dos secuencias diferentes (#$C3#$A4 y 'a'#$CC#$88). Bajo Linux y BSD se puede crear un nombre de fichero con ambas codificaciones. OS X convierte automáticamente la diéresis a una secuencia de tres bytes. Esto significa:
+
&nbsp;&nbsp;&nbsp;Las funciones de fichero de la unidad FileUtil tienen especial cuidado en Mac OS X: OS X normaliza los nombres de fichero. Por ejemplo el nombre de fichero 'ä.txt' puede ser codificado en unicode con dos secuencias diferentes (#$C3#$A4 y 'a'#$CC#$88). Bajo Linux y BSD se puede crear un nombre de fichero con ambas codificaciones. OS X convierte automáticamente la diéresis a una secuencia de tres bytes. Esto significa:
 +
 
 +
<syntaxhighlight lang=pascal> if Filename1=Filename2 then ... // no es suficiente bajo OS X
 +
if AnsiCompareFileName(Filename1,Filename2)=0 then ... // no suficiente bajo fpc 2.2.2, incluso no con cwstring
 +
if CompareFilenames(Filename1,Filename2)=0 then ... // esto siempre funciona (unit FileUtil o FileProcs)</syntaxhighlight>
 +
 
 +
==UTF8 y archivos fuente - el BOM desaparecido==
 +
&nbsp;&nbsp;&nbsp;Al crear archivos de código fuente con Lazarus y escribir algunos caracteres no ASCII el archivo se guarda en UTF8. '''No utiliza BOM''' (marca de orden de byte). Puedes cambiar la codificación en el menú contextual sobre el editor con la opción ''Ajustes de archivo... / Codificación''. La razón de la omisión de BOM es cómo trata Ansistrings FPC. Por compatibilidad la LCL usa Ansistrings y para la portabilidad del LCL utiliza UTF8.
 +
 
 +
&nbsp;&nbsp;&nbsp;Nota:Algunos editores de texto de MS Windows podrían tratar los archivos como con codificación del sistema y mostrar caracteres no válidos. No agregue BOM. Si se agrega BOM tenemos que cambiar todas las asignaciones de cadena.
 +
 
 +
&nbsp;&nbsp;&nbsp;Por ejemplo:
 +
 
 +
<syntaxhighlight lang=pascal> Button1.Caption:='Über';</syntaxhighlight>
 +
 
 +
&nbsp;&nbsp;&nbsp;Cuando no hay BOM (marca de orden de byte) y no nos pasan un parámetro de página de códigos el compilador trata la cadena como sistema de codificación y copia cada byte no convertido a la cadena. Así es como el LCL espera cadenas de texto.
 +
 
 +
<syntaxhighlight lang=pascal> // el archivo fuente está guardado como UTF sin BOM
 +
  if FileExists('Über.txt') then ; // erróneo, porque FileExists espera codificación del sistema
 +
  if FileExistsUTF8('Über.txt') then ; // correcto</syntaxhighlight>
 +
 +
==Widestrings y Ansistrings==
 +
&nbsp;&nbsp;&nbsp;al pasar  de Ansistrings a Widestrings hay que convertir la codificación.
 +
<syntaxhighlight lang=pascal> var
 +
  w: widestring;
 +
  begin
 +
  w:='Über'; // erróneo, porque FPC convertirá la codificación del sistema a UTF16
 +
  w:=UTF8ToUTF16('Über'); // correcto
 +
  Button1.Caption:=UTF16ToUTF8(w);
 +
  end;</syntaxhighlight>
  
<Delphi>
+
==Archivos fuente con UTF8 BOM==
if Filename1=Filename2 then ... // no es suficiente bajo OS X
+
&nbsp;&nbsp;&nbsp;Si haces una gran cantidad de conversiones widestring en una unidad, el código podría ser más legible guardando el fuente como UTF8 con BOM. El compilador permite una codificación por unidad.
if AnsiCompareFileName(Filename1,Filename2)=0 then ... // no suficiente bajo fpc 2.2.2, incluso no con cwstring
 
if CompareFilenames(Filename1,Filename2)=0 then ... // esto siempre funciona (unit FileUtil o FileProcs)
 
</Delphi>
 
  
 
===Lenguajes del este asiático en Windows===
 
===Lenguajes del este asiático en Windows===
  
La fuente por defecto (Tahoma) para los controles de interface de usuario bajo Windows XP son capaces de mostrar correctamente varios lenguajes, incluyendo arabe, ruso y lenguajes del oeste, pero no lenguajes del este tales como chino, japones y coreano.
+
&nbsp;&nbsp;&nbsp;La fuente por defecto (Tahoma) para los controles de interface de usuario bajo Windows XP son capaces de mostrar correctamente varios lenguajes, incluyendo árabe, ruso y lenguajes del oeste, pero no lenguajes del este tales como chino, japones y coreano.
Se puede escoger la fuente para el interface standard de usuario modificando la configuración regional del sistema operativo en el panel de control, seleccionando en la solapa de lenguaje el que se necesite. De esta forma ya se podría visualizar correctamente en pantalla el lenguaje deseado.
+
&nbsp;&nbsp;&nbsp;Se puede escoger la fuente para la interfaz estándar de usuario modificando la configuración regional del sistema operativo en el panel de control, seleccionando en la solapa de lenguaje el que se necesite. De esta forma ya se podría visualizar correctamente en pantalla el lenguaje deseado.
Obviamente las versiones de XP para estos idiomas deben contener dicho paquete de lenguaje instalado. Ver instrucciones en: [http://newton.uor.edu/Departments&Programs/AsianStudiesDept/Language/asianlanguageinstallation_XP.html]
+
&nbsp;&nbsp;&nbsp;Obviamente las versiones de XP para estos idiomas deben contener dicho paquete de lenguaje instalado. Ver instrucciones en: [http://newton.uor.edu/Departments&Programs/AsianStudiesDept/Language/asianlanguageinstallation_XP.html]
 
 
  
 
== Guias de implementación ==
 
== Guias de implementación ==
Line 242: Line 353:
 
=== Requerimientos ===
 
=== Requerimientos ===
  
El espíritu de Lazarus es: "Escribe el código una vez, compila donde quieras."
+
&nbsp;&nbsp;&nbsp;El espíritu de Lazarus es: "Escribe el código una vez, compila donde quieras."
Esto significa, idealmente, que una aplicación con soporte UNICODE habilitado debería tener una sola versión de código fuente soportada, sin defines condicionales en las diferentes plataformas destino.
+
&nbsp;&nbsp;&nbsp;Esto significa, idealmente, que una aplicación con soporte UNICODE habilitado debería tener una sola versión de código fuente soportada, sin defines condicionales en las diferentes plataformas destino.
 
 
La parte "interface" de LCL debería soportar Unicode para las plataformas destino que las soportan ya por si mismas ocultando todas las peculiaridades para el programador de aplicaciones.
 
 
 
En lo que concierne a Lazarus, la comunicación interna de cadenas en los límites "Código de aplicacion <--> LCL", así como "LCL <--> Widgetsets" está basada en las cadenas clásicas (orientadas al byte). Lógicamente, sus contenidos deberían codificarse de acuerdo a [[UTF-8]].
 
  
 +
&nbsp;&nbsp;&nbsp;La parte "interface" de LCL debería soportar Unicode para las plataformas destino que las soportan ya por si mismas ocultando todas las peculiaridades para el programador de aplicaciones.
  
 +
&nbsp;&nbsp;&nbsp;En lo que concierne a Lazarus, la comunicación interna de cadenas en los límites "Código de aplicacion <--> LCL", así como "LCL <--> Widgetsets" está basada en las cadenas clásicas (orientadas al byte). Lógicamente, sus contenidos deberían codificarse de acuerdo a [[UTF-8]].
  
 
=== Migración a UNICODE ===
 
=== Migración a UNICODE ===
  
Las versiones anteriores de Lazarus utilizan codificación ANSI, porque era la opción por defecto para Gtk1 y win32, hasta la versión 0.9.24. Ya con la versión 0.9.25 todos los widgetsets utilizarán UTF-8 por defecto (a excepción de gtk1, que solamente soporta UTF-8 cuando el sistema lo soporta, con sus correspondientes limitaciones). Por ello todas las aplicaciones que pasen cadenas directamente al interface (tanto las escritas en código o directamente mediante el inspector de objetos) necesitarán ser convertidas a UTF-8.
+
&nbsp;&nbsp;&nbsp;Las versiones anteriores de Lazarus utilizan codificación ANSI, porque era la opción por defecto para Gtk1 y win32, hasta la versión 0.9.24. Ya con la versión 0.9.25 todos los widgetsets utilizarán UTF-8 por defecto (a excepción de gtk1, que solamente soporta UTF-8 cuando el sistema lo soporta, con sus correspondientes limitaciones). Por ello todas las aplicaciones que pasen cadenas directamente al interface (tanto las escritas en código o directamente mediante el inspector de objetos) necesitarán ser convertidas a UTF-8.
  
Actualmente tenemos varios grupos de widgetsets, acordes a la codificación:
+
&nbsp;&nbsp;&nbsp;Actualmente tenemos varios grupos de widgetsets, acordes a la codificación:
  
 
*Interfaces que utilizan codificación ANSI: gtk (1) en sistemas ANSI.
 
*Interfaces que utilizan codificación ANSI: gtk (1) en sistemas ANSI.
Line 261: Line 370:
 
*Interfaces que utilizan codificación UTF-8: gtk (1) en sistemas UTF-8: gtk2, qt, fpGUI, carbon, win32, wince, all others.
 
*Interfaces que utilizan codificación UTF-8: gtk (1) en sistemas UTF-8: gtk2, qt, fpGUI, carbon, win32, wince, all others.
  
Ten en cuenta que gtk 1 se encuentra en ambos grupos ANSI y UTF-8. Esto se debe a que la codificación se controla mediante una variable de entorno en Gtk 1.
+
&nbsp;&nbsp;&nbsp;Ten en cuenta que gtk 1 se encuentra en ambos grupos ANSI y UTF-8. Esto se debe a que la codificación se controla mediante una variable de entorno en Gtk 1.
  
Tal como es Lazarus a fecha de hoy, el software existente funcionará si se recompila para los interfaces de win32, winCE o gtk, pero presentará complicaciones de compilación para otros widgetset. Y el nuevo software utilizando UTF-8 funcionará cuando se recompile para cualquiera de los widgesets del grupo UNICODE.
+
&nbsp;&nbsp;&nbsp;Tal como es Lazarus a fecha de hoy, el software existente funcionará si se recompila para los interfaces de win32, winCE o gtk, pero presentará complicaciones de compilación para otros widgetset. Y el nuevo software utilizando UTF-8 funcionará cuando se recompile para cualquiera de los widgesets del grupo UNICODE.
  
El IDE se ha extendido para cargar(load)/salvar(save)/editar(edit) ficheros con las diferentes codificaciones (una codificación por fichero). Tiene una construcción heurística para determinar la codificación y de esta forma se puede cambiar la codificación del fichero en cualquier momento (Source Editor / Popup Menu / File Settings/ Encoding) por lo que el IDE puede abrir ficheros y proyectos antiguos y de esta forma utilizarse para convertir a la codificación deseada.
+
&nbsp;&nbsp;&nbsp;El IDE se ha extendido para cargar(load)/salvar(save)/editar(edit) ficheros con las diferentes codificaciones (una codificación por fichero). Tiene una construcción heurística para determinar la codificación y de esta forma se puede cambiar la codificación del fichero en cualquier momento (Source Editor / Popup Menu / File Settings/ Encoding) por lo que el IDE puede abrir ficheros y proyectos antiguos y de esta forma utilizarse para convertir a la codificación deseada.
  
 
== Recorrido ==
 
== Recorrido ==
  
Ahora que tenemos establecida la guia, es hora de crear un recorrido y ponerlo en práctica. Por ello se ha creado el siguiente plan. Dicho plan divide las tareas en dos grupos. Uno para las tareas primarias y otro para las secundarias.
+
&nbsp;&nbsp;&nbsp;Ahora que tenemos establecida la guia, es hora de crear un recorrido y ponerlo en práctica. Por ello se ha creado el siguiente plan. Dicho plan divide las tareas en dos grupos. Uno para las tareas primarias y otro para las secundarias.
  
Todas las tareas primarias deben implementarse completamente antes de que se pueda decir que Lazarus tiene funcionalidad Unicode, y como tal debe prestarse especial atención a tal finalidad dedicándole el esfuerzo oportuno.
+
&nbsp;&nbsp;&nbsp;Todas las tareas primarias deben implementarse completamente antes de que se pueda decir que Lazarus tiene funcionalidad Unicode, y como tal debe prestarse especial atención a tal finalidad dedicándole el esfuerzo oportuno.
  
Las tareas secundarias son deseables, pero no se implementarán a menos que algunos voluntarios se dediquen a ello o se presenten muchas peticiones.
+
&nbsp;&nbsp;&nbsp;Las tareas secundarias son deseables, pero no se implementarán a menos que algunos voluntarios se dediquen a ello o se presenten muchas peticiones.
  
 
=== Tareas primarias ===
 
=== Tareas primarias ===
  
 +
&nbsp;&nbsp;&nbsp;'''Hacer que Win32 Widgetset soporte UTF-8'''
  
'''Hacer que Win32 Widgetset soporte UTF-8'''
+
&nbsp;&nbsp;&nbsp;Notas: en este paso nos centraremos en todas las versiones de 32 bits de Windows al mismo tiemo. Todo el código generado en este esfuerzo será aislado del actual interface win32 mediante IFDEFs, para evitar introducir errores en este interface primario. Después del tiempo de transición, los IFDEFs serán removidos y únicamente se mantendrá el código UNICODE.
 
 
Notas: en este paso nos centraremos en todas las versiones de 32 bits de Windows al mismo tiemo. Todo el código generado en este esfuerzo será aislado del actual interface win32 mediante IFDEFs, para evitar introducir errores en este interface primario. Después del tiempo de transición, los IFDEFs serán removidos y únicamente se mantendrá el código UNICODE.
 
 
   
 
   
 +
&nbsp;&nbsp;&nbsp;Estado: totalmente implementado.
  
Estado: totalmente implementado.
+
&nbsp;&nbsp;&nbsp;'''Actualiza las funciones de teclado Gtk 2 para que trabajen con UTF-8'''
 
 
  
'''Actualiza las funciones de teclado Gtk 2 para que trabajen con UTF-8'''
+
&nbsp;&nbsp;&nbsp;Notas:
  
Notas:
+
&nbsp;&nbsp;&nbsp;Estado: casi completo. Algunas características pre-editadas de gtk2 no están todavía soportadas en los controles customizados. Desconozco que lenguaje necesitan.
  
Estado: casi completo. Algunas características pre-editadas de gtk2 no están todavía soportadas en los controles customizados. Desconozco que lenguaje necesitan.
+
&nbsp;&nbsp;&nbsp;''' Hay que asegurarse de que el IDE de Lazarus funciona correctamente con el widgetset UNICODE de Win32 y que soporte UTF-8'''
  
''' Hay que asegurarse de que el IDE de Lazarus funciona correctamente con el widgetset UNICODE de Win32 y que soporte UTF-8'''
+
&nbsp;&nbsp;&nbsp;Notas:
  
Notas:  
+
&nbsp;&nbsp;&nbsp;Estado: completo. Excepto por el mapa de caracteres, el cual muestra solamente 255 caracteres. De todas formas todos los sistemas operativos modernos proveen mapas de caracteres manejables.
  
Estado: completo. Excepto por el mapa de caracteres, el cual muestra solamente 255 caracteres. De todas formas todos los sistemas operativos modernos proveen mapas de caracteres manejables.
+
&nbsp;&nbsp;&nbsp;''' Hay que asegurarse de que el IDE de Lazarus funciona correctamente con el widgetset de Gtk2 y de si soporta UTF-8'''
  
''' Hay que asegurarse de que el IDE de Lazarus funciona correctamente con el widgetset de Gtk2 y de si soporta UTF-8'''
+
&nbsp;&nbsp;&nbsp;Notas:
  
 
+
&nbsp;&nbsp;&nbsp;Estado: completo. Hay errores gtk2 intf, pero no tienen nada que ver con UTF-8.
Notas:
 
 
 
Estado: completo. Hay errores gtk2 intf, pero no tienen nada que ver con UTF-8.
 
  
 
=== Tareas secundarias ===
 
=== Tareas secundarias ===
  
''' Actualizar el widgetset Windows CE para que utilice UTF-8'''
+
&nbsp;&nbsp;&nbsp;''' Actualizar el widgetset Windows CE para que utilice UTF-8'''
  
Notas: las rutinas de conversión de rutinas están concentrados en el fichero winceproc.pp. Se necesitan algunos tests.
+
&nbsp;&nbsp;&nbsp;Notas: las rutinas de conversión de rutinas están concentrados en el fichero winceproc.pp. Se necesitan algunos tests.
  
Estado: completado.
+
&nbsp;&nbsp;&nbsp;Estado: completado.
  
''' Actualiza las funciones de teclado de Gtk 1 para que trabajen con UTF-8'''
+
&nbsp;&nbsp;&nbsp;''' Actualiza las funciones de teclado de Gtk 1 para que trabajen con UTF-8'''
  
Notas:
+
&nbsp;&nbsp;&nbsp;Notas:
  
Estado: no implementado.
+
&nbsp;&nbsp;&nbsp;Estado: no implementado.
  
'''RTL completa en synedit'''
+
&nbsp;&nbsp;&nbsp;'''RTL completa en synedit'''
  
Notas: RTL significa de derecha a izquierda como por ejemplo el utilizado en árabe.
+
&nbsp;&nbsp;&nbsp;Notas: RTL significa de derecha a izquierda como por ejemplo el utilizado en árabe.
  
Estado: no implementado.
+
&nbsp;&nbsp;&nbsp;Estado: no implementado.
  
 
== Esenciales de UNICODE ==
 
== Esenciales de UNICODE ==
  
UNICODE estándar mapea enteros (integers) de 0 a 10FFFF (h) a caracteres. Cada mapeo es llamado un punto de codigo. En otras palabras, los caracteres UNICODE están en principio definidos por puntos de código desde U+000000 a U+10FFFF (0 a 1 114 111).
+
&nbsp;&nbsp;&nbsp;UNICODE estándar mapea enteros (integers) de 0 a 10FFFF (h) a caracteres. Cada mapeo es llamado un punto de codigo. En otras palabras, los caracteres UNICODE están en principio definidos por puntos de código desde U+000000 a U+10FFFF (0 a 1 114 111).
 
Unicode standard maps integers from 0 to 10FFFF(h) to characters. Each such mapping is called a code point. In other words, Unicode characters are in principle defined for code points from U+000000 to U+10FFFF en hexadecimal (0 to 1.114.111 en decimal).
 
Unicode standard maps integers from 0 to 10FFFF(h) to characters. Each such mapping is called a code point. In other words, Unicode characters are in principle defined for code points from U+000000 to U+10FFFF en hexadecimal (0 to 1.114.111 en decimal).
  
Existen tres esquemas para representar punto de código UNICODE como una única secuencia de bytes. Estos esquemas se llaman formatos de transformación UNICODE: UTF-8, UTF-16 y UTF-32. La conversión entre todos ellos es posible. Aquí están sus propiedades básicas:
+
&nbsp;&nbsp;&nbsp;Existen tres esquemas para representar punto de código UNICODE como una única secuencia de bytes. Estos esquemas se llaman formatos de transformación UNICODE: UTF-8, UTF-16 y UTF-32. La conversión entre todos ellos es posible. Aquí están sus propiedades básicas:
  
                            UTF-8 UTF-16 UTF-32
+
                            UTF-8 UTF-16 UTF-32
Smallest code point [hex] 000000 000000 000000
+
  Smallest code point [hex] 000000 000000 000000
Largest code point  [hex] 10FFFF 10FFFF 10FFFF
+
  Largest code point  [hex] 10FFFF 10FFFF 10FFFF
Code unit size [bits]          8    16    32
+
  Code unit size [bits]          8    16    32
Minimal bytes/character        1      2      4
+
  Minimal bytes/character        1      2      4
Maximal bytes/character        4      4      4
+
  Maximal bytes/character        4      4      4
  
'''UTF-8''' tiene varias propiedades importantes y útiles: es interpretado como una secuencia de bytes, por lo que el concepto de orden bajo(lo-) y alto (hi-) no existe. Los caracteres U+0000 a U+007F (ASCII)se codifican simplemente como bytes 00h a 7Fh (por compatibilidad ASCII). Esto significa que los ficheros y cadenas que contienen solamente caracteres de 7-bit ASCII tienen la misma codificación en ambos formatos (ASCII y UTF-8). Todos los caracteres >U+007F se codifican como una secuencia de varios bytes, cada uno de los cuales tiene los dos bits más significativos puestos a 1 (bits set). No se contiene una secuencia de bytes de un carácter dentro de la larga secuencia de bytes del otro. Esto permite la búsqueda sencilla de subcadenas. El primer byte de una secuencia multibyte que representa un carácter no-ASCII está siempre en el rango C0h a FDh y esto indica cuantos bytes siguen a este carácter. Todos los caracteres que que siguen en una secuencia multibyte se encuentran en el rango 80h a BFh. Esto permite una fácil resincronización y robustez.
+
&nbsp;&nbsp;&nbsp;'''UTF-8''' tiene varias propiedades importantes y útiles: es interpretado como una secuencia de bytes, por lo que el concepto de orden bajo(lo-) y alto (hi-) no existe. Los caracteres U+0000 a U+007F (ASCII)se codifican simplemente como bytes 00h a 7Fh (por compatibilidad ASCII). Esto significa que los ficheros y cadenas que contienen solamente caracteres de 7-bit ASCII tienen la misma codificación en ambos formatos (ASCII y UTF-8). Todos los caracteres >U+007F se codifican como una secuencia de varios bytes, cada uno de los cuales tiene los dos bits más significativos puestos a 1 (bits set). No se contiene una secuencia de bytes de un carácter dentro de la larga secuencia de bytes del otro. Esto permite la búsqueda sencilla de subcadenas. El primer byte de una secuencia multibyte que representa un carácter no-ASCII está siempre en el rango C0h a FDh y esto indica cuantos bytes siguen a este carácter. Todos los caracteres que que siguen en una secuencia multibyte se encuentran en el rango 80h a BFh. Esto permite una fácil resincronización y robustez.
  
'''UTF-16''' tiene las siguientes propiedades más destacadas. Utiliza una sola palabra (words) de 16-bit para codificar caracteres desde U+0000 a U+d7ff, y un par de palabras de 16 bits para codificar cualquiera de los caracteres UNICODE restantes.  
+
&nbsp;&nbsp;&nbsp;'''UTF-16''' tiene las siguientes propiedades más destacadas. Utiliza una sola palabra (words) de 16-bit para codificar caracteres desde U+0000 a U+d7ff, y un par de palabras de 16 bits para codificar cualquiera de los caracteres UNICODE restantes.  
 
remaining Unicode characters.
 
remaining Unicode characters.
  
Finalmente, cualquier caracter UNICODE puede ser representado como una unidad de 32 bits en '''UTF-32'''.
+
&nbsp;&nbsp;&nbsp;Finalmente, cualquier caracter UNICODE puede ser representado como una unidad de 32 bits en '''UTF-32'''.
  
Para más información, ver:
+
&nbsp;&nbsp;&nbsp;Para más información, ver:
 
   
 
   
[http://www.unicode.org/faq/basic_q.html Unicode FAQ - Basic questions],
+
&nbsp;&nbsp;&nbsp;[http://www.unicode.org/faq/basic_q.html Unicode FAQ - Basic questions],
[http://www.unicode.org/faq/utf_bom.html Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM],
+
&nbsp;&nbsp;&nbsp;[http://www.unicode.org/faq/utf_bom.html Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM],
[http://en.wikipedia.org/wiki/UTF-8 Wikipedia: UTF-8]
+
&nbsp;&nbsp;&nbsp;[http://en.wikipedia.org/wiki/UTF-8 Wikipedia: UTF-8]
[http://en.wikipedia.org/wiki/ISO-8859]
+
&nbsp;&nbsp;&nbsp;[http://en.wikipedia.org/wiki/ISO-8859]
  
== Lazarus component library architecture essentials ==
+
==Lo esencial de la arquitectura de la librería de componentes de Lazarus ==
  
The LCL consists of two parts:  
+
&nbsp;&nbsp;&nbsp;La LCL consta de dos partes:  
# A target platform independent part, which implements a class hierarchy analogous to Delphi VCL;  
+
# Una parte independiente de la plataforma de destino, que implementa un jerarquía de clases análoga a la VCL de Delphi;  
# "Interfaces" - a part that implements the interface to APIs of each target platform.
+
# "Interfaces" - la parte que implementa la interfaz a las APIs de cada plataforma de destino.
  
The communication between the two parts is done by an abstract class TWidgetset. Each widgetset is implemented by its own class derived from TWidgetset.
+
&nbsp;&nbsp;&nbsp;La comunicación entre las dos partes se realiza mediante una clase abstracta TWidgetset. Cada widgetset se implementa por su propia clase derivada de TWidgetset.
  
The GTK 1 widgetset is the oldest. In this widgetset the string encoding is determined by the LANG environment variable, which is usually a iso The ISO-8859-n group of single byte encodings. Recently (as of Mandriva 2007, for example), many distributions have being shipping Gtk 1 configured for UTF-8. Our Gtk 1 interface lacks proper support for UTF-8 on the keyboard handling routines, so this is a big problem, that increases the need for Lazarus to implement cross-platform Unicode support.
+
&nbsp;&nbsp;&nbsp;El widgetset de GTK 1 es el más antiguo.  
 +
En este widgetset se determina la codificación de los textos mediante la variable de entorno LANG, que suele ser un grupo iso ISO-8859-n de un solo byte de codificación. Recientemente (a partir de Mandriva 2007, por ejemplo), muchas distribuciones han distribuido Gtk 1 configurado para UTF-8. Nuestra interfaz Gtk 1 carece de un apoyo eficaz a UTF-8 en las rutinas de manejo del teclado, así que esto es un gran problema, que aumenta la necesidad de Lazarus de implementar soporte Unicode multiplataforma.
  
Gtk2 widgetset only works with UTF-8 encoding and supports UTF-8 completely.
+
&nbsp;&nbsp;&nbsp;El widgetset de Gtk2 únicamente funciona con codificación UTF-8 y suporta UTF-8 completamente.
  
The win32 interface is setup with ansi widgets and UTF-8 support is started, but not yet complete and therefore disabled by default. So it is currently not possible to use Unicode with win32.
+
&nbsp;&nbsp;&nbsp;La interfaz win32 dispone de widgets ansi y el soporte UTF-8 se ha iniciado, pero no
 +
interface is setup with ansi widgets and UTF-8 support is started, pero no esta acabado y está deshabilitado. Por lo tanto, actualmente no es posible utilizar Unicode con win32.
  
Qt interface is prepared for UTF-8. Qt itself uses UTF-16 as native encoding, but the lazarus interface for Qt converts from UTF-8 to UTF-16.
+
&nbsp;&nbsp;&nbsp;La interfaz Qt está preparada para UTF-8. El propio Qt usa UTF-16 como codificación nativa, pero la interfaz de Lazarus para Qt convierte de UTF-8 a UTF-16.
  
Windows CE only support UTF-16 as character encoding, but our interface for it currently converts strings from ISO to UTF-16 before calling the Windows API. This is very easy to fix, as all conversion code is concentrated on a few routines on the winceproc.pp file.
+
&nbsp;&nbsp;&nbsp;Windows CE únicamente soporta UTF-16 pra la codificación de caracteres, pero nuestra interfaz para él ahora convierte las cadenas de ISO a UTF-16 antes de llamar a la API de Windows. Esto es muy fácil de solucionar, ya que todo el código de conversión se concentra en unas pocas rutinas en el archivo winceproc.pp.
  
For more, see: [[LCL Internals#Internals of the LCL|Internals of the LCL]]
+
&nbsp;&nbsp;&nbsp;For more, see: [[LCL Internals#Internals of the LCL|Internals of the LCL]]
  
 
== Habilitando UNICODE para el interface win32 ==
 
== Habilitando UNICODE para el interface win32 ==
Line 377: Line 484:
 
=== Compilando LCL-Win32 con UNICODE ===
 
=== Compilando LCL-Win32 con UNICODE ===
  
Para habilitar UNICODE en LCL para Windows ir al menú, seleccionar entrada "Herramientas" --> "Configurar "Construir Lazarus..." poner dWindowsUnicodeSupport en el campo "Opciones". Selecccionar todos los objetivos a NONE, y solo LCL a Clena+Build. Selecciona win32 como objetivo wdgetset. Finalmente pulsar en "Construir".
+
&nbsp;&nbsp;&nbsp;Para habilitar UNICODE en LCL para Windows ir al menú, seleccionar entrada "Herramientas" --> "Configurar "Construir Lazarus..." poner dWindowsUnicodeSupport en el campo "Opciones". Selecccionar todos los objetivos a NONE, y solo LCL a Clean+Build. Selecciona win32 como objetivo wdgetset. Finalmente pulsar en "Construir".
 
 
Ahora ya puedes recompilar tus aplicaciones pre-existentes y tendrán el modo UNICODE habilitado.
 
 
 
Nota: Desde la versión 0.9.25 revisión 14883 esta operación no es necesaria nunca más debido a que Lazarus tiene el soporte UNICODE habilitado por defecto.
 
 
 
=== Guidelines ===
 
  
Primera, y más importante, todos los parches UNICODE para el interface Win32 deben ser incluidos por IFDEF WindowsUnicodeSupport, para evitar romper el interface ANSI existente. Después de que este se estabilice, todos los ifdefs deben ser quitados y solamente permanecerá la parte UNICODE. En este momento todos los programas existentes que usan caracteres ANSI necesitan migración a UNICODE.
+
&nbsp;&nbsp;&nbsp;Ahora ya puedes recompilar tus aplicaciones pre-existentes y tendrán el modo UNICODE habilitado.
  
 +
&nbsp;&nbsp;&nbsp;'''Nota''': Desde la versión 0.9.25 revisión 14883 esta operación no es necesaria debido a que Lazarus tiene el soporte UNICODE habilitado por defecto.
  
Las plantaformas windows <=Win9x se basan en códigos de página ISO.
+
=== Directrices ===
standards and only partially support Unicode. Las plataformas windows comenzando con WinNT y Windows CE suportan completamente UNICODE. Win 9x y NT ofrecen dos conjuntos paralelos de funciones API: el antiguo ANSI abilitado *A y el nuevo, UNICODE habilitado *W. Las funciones *W aceptan wide strings, por ejemplo cadenas codificadas UTF-16 como parámetros. Windows CE solamente utiliza funciones wide API.
+
==== ====
 +
&nbsp;&nbsp;&nbsp;Primera, y más importante, todos los parches UNICODE para el interface Win32 deben ser incluidos por '''{$ifdef WindowsUnicodeSupport}''', para evitar romper el interface ANSI existente. Cuándo el código sea estable, todos los ''ifdef'' se quitaran y solamente permanecerá la parte UNICODE. En este momento todos los programas existentes que usan caracteres ANSI necesitaran migrar a UNICODE.
  
starting with WinNT and Windows CE fully support Unicode. Win 9x and NT offer two parallel sets of API functions:  
+
&nbsp;&nbsp;&nbsp;Las plantaformas windows <=Win9x se basan en códigos de página de la norma ISO y soportan Unicode de forma parcial. Las plataformas ''win'' a partir de WinNT y Windows CE suportan completamente UNICODE. Win 9x y NT ofrecen dos conjuntos paralelos de funciones API: el antiguo ANSI demoninado *A y el nuevo, UNICODE demonimado *W. Las funciones *W aceptan ''wide strings'', por ejemplo cadenas codificadas UTF-16 como parámetros. Windows CE solamente utiliza funciones Wide API.
  
 
==== Funciones Wide presentes en Windows 9x ====
 
==== Funciones Wide presentes en Windows 9x ====
  
Algunas funciones Wide API están presentes en Windows 9x. La siguiente es una lista de las mismas: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/other_existing_unicode_support.asp
+
&nbsp;&nbsp;&nbsp;Algunas funciones Wide API están presentes en Windows 9x. La siguiente es una lista de las mismas: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/other_existing_unicode_support.asp
  
Ejemplo de conversión:
+
&nbsp;&nbsp;&nbsp;Ejemplo de conversión:
  
<pre>
+
<syntaxhighlight lang=pascal> GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
  GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
+
Length(ButtonCaption), TextSize);</syntaxhighlight>
Length(ButtonCaption), TextSize);
 
</pre>
 
  
Viene a ser:
+
&nbsp;&nbsp;&nbsp;Viene a ser:
  
<pre>
+
<syntaxhighlight lang=pascal> {$ifdef WindowsUnicodeSupport}
  {$ifdef WindowsUnicodeSupport}
 
 
     GetTextExtentPoint32W(hdcNewBitmap, PWideChar(Utf8Decode(ButtonCaption)), Length(WideCaption), TextSize);
 
     GetTextExtentPoint32W(hdcNewBitmap, PWideChar(Utf8Decode(ButtonCaption)), Length(WideCaption), TextSize);
 
   {$else}
 
   {$else}
 
     GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption), Length(ButtonCaption), TextSize);
 
     GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption), Length(ButtonCaption), TextSize);
   {$endif}
+
   {$endif}</syntaxhighlight>
</pre>
 
  
 
==== Funciones que necesitan versiones ANSI y Wide ====
 
==== Funciones que necesitan versiones ANSI y Wide ====
  
Primer ejemplo de conversión:
+
&nbsp;&nbsp;&nbsp;Primer ejemplo de conversión:
  
<pre>
+
<syntaxhighlight lang=pascal> function TGDIWindow.GetTitle: String;
function TGDIWindow.GetTitle: String;
+
var
var
+
  l: Integer;
l: Integer;
+
begin
begin
 
 
   l := Windows.GetWindowTextLength(Handle);
 
   l := Windows.GetWindowTextLength(Handle);
 
   SetLength(Result, l);
 
   SetLength(Result, l);
 
   Windows.GetWindowText(Handle, @Result[1], l);
 
   Windows.GetWindowText(Handle, @Result[1], l);
end;
+
end;</syntaxhighlight>
</pre>
 
  
Viene a ser así:
+
&nbsp;&nbsp;&nbsp;Viene a ser así:
  
<pre>
+
<syntaxhighlight lang=pascal> function TGDIWindow.GetTitle: String;
function TGDIWindow.GetTitle: String;
+
var
var
+
  l: Integer;
l: Integer;
+
  AnsiBuffer: string;
AnsiBuffer: string;
+
  WideBuffer: WideString;
WideBuffer: WideString;
+
begin
begin
 
  
{$ifdef WindowsUnicodeSupport}
+
{$ifdef WindowsUnicodeSupport}
  
if UnicodeEnabledOS then
+
  if UnicodeEnabledOS then
begin
+
  begin
  l := Windows.GetWindowTextLengthW(Handle);
+
    l := Windows.GetWindowTextLengthW(Handle);
  SetLength(WideBuffer, l);
+
    SetLength(WideBuffer, l);
  l := Windows.GetWindowTextW(Handle, @WideBuffer[1], l);
+
    l := Windows.GetWindowTextW(Handle, @WideBuffer[1], l);
  SetLength(WideBuffer, l);
+
    SetLength(WideBuffer, l);
  Result := Utf8Encode(WideBuffer);
+
    Result := Utf8Encode(WideBuffer);
end
+
  end
else
+
  else
begin
+
  begin
  l := Windows.GetWindowTextLength(Handle);
+
    l := Windows.GetWindowTextLength(Handle);
  SetLength(AnsiBuffer, l);
+
    SetLength(AnsiBuffer, l);
  l := Windows.GetWindowText(Handle, @AnsiBuffer[1], l);
+
    l := Windows.GetWindowText(Handle, @AnsiBuffer[1], l);
  SetLength(AnsiBuffer, l);
+
    SetLength(AnsiBuffer, l);
  Result := AnsiToUtf8(AnsiBuffer);
+
    Result := AnsiToUtf8(AnsiBuffer);
end;
+
  end;
  
 
{$else}
 
{$else}
  
  l := Windows.GetWindowTextLength(Handle);
+
  l := Windows.GetWindowTextLength(Handle);
  SetLength(Result, l);
+
  SetLength(Result, l);
  Windows.GetWindowText(Handle, @Result[1], l);
+
  Windows.GetWindowText(Handle, @Result[1], l);
  
 
{$endif}
 
{$endif}
  
end;
+
end;</syntaxhighlight>
 
 
</pre>
 
  
 
=== Recorrido ===
 
=== Recorrido ===
Line 487: Line 581:
  
 
* SynEdit no soporta RTL de derecha a izquierda (Right To Left)
 
* SynEdit no soporta RTL de derecha a izquierda (Right To Left)
* ¿Ha sido OpenFileDialogCallBack testeado con un largo número de ficheros?
+
*<s> ¿Ha sido OpenFileDialogCallBack testeado con un largo número de ficheros?
 
** ¿Es esto un problema específico de UNICODE? Yo creo que es un problema genérico. --[[User:Sekelsenmat|Sekelsenmat]] 13:40, 14 February 2008 (CET)
 
** ¿Es esto un problema específico de UNICODE? Yo creo que es un problema genérico. --[[User:Sekelsenmat|Sekelsenmat]] 13:40, 14 February 2008 (CET)
 
*** Puede ser. Lo he testeado con una gran cantidad de ficheros antes de añadir la versión UNICODE. Si se trata de un problema genérico, entonces la versión no-UNICODE estaría rota cuando la versión unicode fue añadida. [[User:Vincent|Vincent]] 21:45, 15 February 2008 (CET)
 
*** Puede ser. Lo he testeado con una gran cantidad de ficheros antes de añadir la versión UNICODE. Si se trata de un problema genérico, entonces la versión no-UNICODE estaría rota cuando la versión unicode fue añadida. [[User:Vincent|Vincent]] 21:45, 15 February 2008 (CET)
**** Elemento asociado del seguidor de errores: http://bugs.freepascal.org/view.php?id=10918
+
**** Elemento asociado del seguidor de errores: http://bugs.freepascal.org/view.php?id=10918</s> Corregido e implementado.
Corregido e implementado.
+
*<s> Función de clase TWin32WSSelectDirectoryDialog.CreateHandle: Title, nombre de fichero(FileName) y directorio inicial (InitialDir) debería ser convertido a UNICODE.
* Función de clase TWin32WSSelectDirectoryDialog.CreateHandle: Title, nombre de fichero(FileName) y directorio inicial (InitialDir) debería ser convertido a UNICODE.
+
** Associated bugtracker item: http://bugs.freepascal.org/view.php?id=10919</s> Implementado.
** Associated bugtracker item: http://bugs.freepascal.org/view.php?id=10919 Implementado.
 
  
'''Posibles problemas con el soporte UNICODE'''
+
&nbsp;&nbsp;&nbsp;'''Posibles problemas con el soporte UNICODE'''
  
Basandose en la revisión de código, lo siguiente necesita ser testeado porque el código parece no estar preparado para UNICODE:
+
&nbsp;&nbsp;&nbsp;Basandose en la revisión de código, lo siguiente necesita ser testeado porque el código parece no estar preparado para UNICODE:
  
* Procedimiento de clase TWin32WSCustomComboBox.SetText
+
* <s>Procedimiento de clase TWin32WSCustomComboBox.SetText
 
* TWin32WSCustomTrayIcon.Show: ATrayIcon.Hint no soporta UNICODE
 
* TWin32WSCustomTrayIcon.Show: ATrayIcon.Hint no soporta UNICODE
* TWin32WidgetSet.MessageBox doesn't call MessageBoxW.
+
* TWin32WidgetSet.MessageBox doesn't call MessageBoxW.</s>
 
* TWin32WidgetSet.TextOut: es Windows.TextOut soportado en windows 9X?
 
* TWin32WidgetSet.TextOut: es Windows.TextOut soportado en windows 9X?
** Si, por favor ver [[LCL_Unicode_Support#Wide_functions_present_on_Windows_9x]]. Althought I never tested this.
+
** Si, por favor ver [[LCL_Unicode_Support/es#Wide_functions_present_on_Windows_9x]]. Althought I never tested this.
 
* MessageBox buttons no muestran el UNICODE cuando son traducidos. Testeados en el IDE. Puede haber un problema en el IDE.
 
* MessageBox buttons no muestran el UNICODE cuando son traducidos. Testeados en el IDE. Puede haber un problema en el IDE.
 
** Nota: no he podido reproducirlo utilizando la traducción portuguesa. --[[User:Sekelsenmat|Sekelsenmat]] 22:20, 12 January 2008 (CET)
 
** Nota: no he podido reproducirlo utilizando la traducción portuguesa. --[[User:Sekelsenmat|Sekelsenmat]] 22:20, 12 January 2008 (CET)
Line 510: Line 603:
 
=== Pantallazos ===
 
=== Pantallazos ===
  
 
+
&nbsp;&nbsp;&nbsp;[[Image:Lazarus Unicode Test.png]]
[[Image:Lazarus Unicode Test.png]]
 
  
 
== Ver también ==
 
== Ver también ==
  
 
* [[UTF-8]] - Descripción de cadenas UTF-8.
 
* [[UTF-8]] - Descripción de cadenas UTF-8.

Latest revision as of 01:19, 19 February 2020

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) 한국어 (ko) русский (ru) 中文(中国大陆)‎ (zh_CN) 中文(台灣)‎ (zh_TW)

Introducción

   En la versión 0.9.25, Lazarus tiene soporte completo para Unicode en todas las plataformas a excepción de Gtk 1. En esta página se pueden encontrar instrucciones para usuarios de Lazarus, roadmaps, descripción de conceptos básicos y detalles de implementación.

Instrucciones para usuarios

   En los widgetsets unicode es importante hacer ver que no todo es Unicode. Las librerías Runtime Free Pascal y FCL son ANSI. Es responsabilidad del desarrollador conocer cual es la codificación de sus cadenas y realizar la conversión apropiada entre librerías que esperan diferentes codificaciones.

   Usualmente la codificación es por librería. Cada librería esperará uniformemente un tipo de codificación, que será habitualmente o bien UNICODE (UTF-8 para Lazarus) o ANSI (lo cual significa que es la codificación del sistema, que puede ser UTF-8 o no). Tanto RTL como FCL de Free Pascal Compiler (FPC) 2.2.2 esperan cadenas ANSI (también FPC 2.3.x).

   Se puede realizar la conversión entre UNICODE y ANSI utilizando las funciones UTF8ToAnsi y AnsiToUTF8 de la unit System o bien las funciones UTF8ToSys y SysToUTF8 de la unidad FileUtil. Las dos últimas son más elegantes pero insertan más código en el programa.

   Ejemplos:

   Digamos que tienes una cadena desde un TEdit y necesitas pasársela a alguna rutina de fichero rtl:

 var
   miCadena: string; // codificación utf-8 
 begin
   miCadena := miTEdit.Text;
   unaRutinaRTL(UTF8ToAnsi(miCadena));
 end;

   Y en el modo inverso:

 var
   miCadena: string; // codificada ansi
 begin
   miCadena := unaRutinaRTL;
   miTEdit.Text := AnsiToUTF8(miCadena);
 end;

   Importante: UTF8ToAnsi retornará una cadena vacía si la cadena UTF8 contiene caracteres no válidos.

   Importante: AnsiToUTF8 y UTF8ToAnsi requiere un gestor de cadenas largas bajo Linux, BSD y Mac OS X. Puedes utilizar las funciones SysToUTF8 y UTF8ToSys (unidad FileUtil) o añadir un gestor de cadenas largas mediante cwstring como unidad en la sección uses.

Tratamiento de cadenas UTF-8 y de caracteres

   Si deseamos actuar sobre los caracteres de una cadena UTF8, básicamente hay dos maneras.

  • actuar sobre los bytes - útil para buscar una subcadena o cuando se busca sólo en los caracteres ASCII de la cadena UTF8. Por ejemplo, al analizar archivos xml.
  • actuar a través de los caracteres - útil para componentes gráficos como SynEdit. Por ejemplo cuando se desea conocer el tercer carácter que aparece en la pantalla.

Buscando una subcadena

   Debido a la naturaleza especial de UTF8 podemos simplemente usar las funciones normales de cadena:

 procedure Where(SearchFor, aText: string);
 var
  BytePos: LongInt;
  CharacterPos: LongInt;
 begin
  BytePos:=Pos(SearchFor,aText);
  CharacterPos:=UTF8Length(PChar(aText),BytePos-1);
  writeln('La subcadena "',SearchFor,'" está en el texto "',aText,'"',
    ' en la posición de byte ',BytePos,' y en la posición de caracter ',CharacterPos);
 end;

Accediendo a los caracteres UTF8

   Los caracteres Unicode pueden variar en longitud, por lo que la mejor solución para acceder a ellos es usar una iteración cuando queremos acceder a los caracteres en la secuencia en la que se encuentran. Para ello podemos utilizar este código:

 uses lazutf8; // LCLProc para Lazarus 0.9.30 o inferior
 ...
 procedure DoSomethingWithString(AnUTF8String: string);
 var
  p: PChar;
  CharLen: integer;
  FirstByte, SecondByte, ThirdByte: Char;
 begin
  p:=PChar(AnUTF8String);
  repeat
    CharLen := UTF8CharacterLength(p);

    // Ahora tenemos un puntero al caracter y su longitud
    // Podemos acceder a los bytes del caracter UTF-8 así:
    if CharLen >= 1 then FirstByte := P[0];
    if CharLen >= 2 then SecondByte := P[1];
    if CharLen >= 3 then ThirdByte := P[2];

    inc(p,CharLen);
  until (CharLen=0) or (p^ = #0);
 end;

Acceder al enésimo caracter UTF8

   Además de la iteración, podemos querer tambien realizar un acceso aleatorio a los caracteres UTF-8.

 uses lazutf8; // LCLProc para Lazarus 0.9.30 o inferior
 ...
 var
  AnUTF8String, NthChar: string;
 begin
  NthChar := UTF8Copy(AnUTF8String, N, 1);

Iterar sobre los puntos de código usando UTF8CharacterToUnicode

   A continuación se muestra cómo recorrer en iteración el valor del código de 32 bits de punto para cada carácter de una cadena UTF8:

 uses lazutf8; // LCLProc para Lazarus 0.9.30 o inferior
 ...
 procedure IterateUTF8Characters(const AnUTF8String: string);
 var
  p: PChar;
  unicode: Cardinal;
  CharLen: integer;
 begin
  p:=PChar(AnUTF8String);
  repeat
    unicode:=UTF8CharacterToUnicode(p,CharLen);
    writeln('Unicode=',unicode);
    inc(p,CharLen);
  until (CharLen=0) or (unicode=0);
 end;

UTF-8: Copiar cadena, Longitud, Minúsculas, etc

   Casi todas las operaciones que podríamos querer ejecutar con cadenas UTF-8 están cubiertas por las rutinas en la unidad de lazutf8 (unidad LCLProc en Lazarus 0.9.30 o inferior). Consulte la siguiente lista de las rutinas de tomada de lazutf8.pas:

 function UTF8CharacterLength(p: PChar): integer;
 function UTF8Length(const s: string): PtrInt;
 function UTF8Length(p: PChar; ByteCount: PtrInt): PtrInt;
 function UTF8CharacterToUnicode(p: PChar; out CharLen: integer): Cardinal;
 function UnicodeToUTF8(u: cardinal; Buf: PChar): integer; inline;
 function UnicodeToUTF8SkipErrors(u: cardinal; Buf: PChar): integer;
 function UnicodeToUTF8(u: cardinal): shortstring; inline;
 function UTF8ToDoubleByteString(const s: string): string;
 function UTF8ToDoubleByte(UTF8Str: PChar; Len: PtrInt; DBStr: PByte): PtrInt;
 function UTF8FindNearestCharStart(UTF8Str: PChar; Len: integer;
                                  BytePos: integer): integer;
 // encontrar el caracter enésimo UTF8, ignorando BIDI
 function UTF8CharStart(UTF8Str: PChar; Len, CharIndex: PtrInt): PChar;
 // encontrar el ínidice de bytedel enésimo caracter UTF8, ignorando BIDI (longitud en bytes de la subcadena)
 function UTF8CharToByteIndex(UTF8Str: PChar; Len, CharIndex: PtrInt): PtrInt;
 procedure UTF8FixBroken(P: PChar);
 function UTF8CharacterStrictLength(P: PChar): integer;
 function UTF8CStringToUTF8String(SourceStart: PChar; SourceLen: PtrInt) : string;
 function UTF8Pos(const SearchForText, SearchInText: string): PtrInt;
 function UTF8Copy(const s: string; StartCharIndex, CharCount: PtrInt): string;
 procedure UTF8Delete(var s: String; StartCharIndex, CharCount: PtrInt);
 procedure UTF8Insert(const source: String; var s: string; StartCharIndex: PtrInt); 

 function UTF8LowerCase(const AInStr: string; ALanguage: string=''): string;
 function UTF8UpperCase(const AInStr: string; ALanguage: string=''): string;
 function FindInvalidUTF8Character(p: PChar; Count: PtrInt;
                                  StopOnNonASCII: Boolean = false): PtrInt;
 function ValidUTF8String(const s: String): String;

 procedure AssignUTF8ListToAnsi(UTF8List, AnsiList: TStrings);

 //funciones de comparación

 function UTF8CompareStr(const S1, S2: string): Integer;
 function UTF8CompareText(const S1, S2: string): Integer;

Tratando con directorios y nombres de fichero

   Las funciones y controles de Lazarus esperan nombres de directorios y ficheros con la codificación UTF-8, pero RTL utiliza cadenas ANSI para los mismos.

   Por ejemplo, consideremos un botón, que establece la propiedad directorio de TFileListBox al valor del directorio actual. La función RTL GetCurrentDir es ANSI, y no UNICODE, lo cual precisa de una conversión:

 procedure TForm1.Boton1Click(Sender: TObject);
 begin
   FileListBox1.Directory:=SysToUTF8(GetCurrentDir);
   // o usar las funciones de la unidad FileUtil
   FileListBox1.Directory:=GetCurrentDirUTF8;
 end;

   La unidad FileUtil define funciones comunes de fichero con cadenas UTF-8:

 // funciones básicas similares a la RTL pero trabajando con UTF-8 en lugar de
 // codif cación de sistema
 // AnsiToUTF8 y UTF8ToAnsi necesitan un gestor de cadenas largas (widestring) bajo Linux, BSD, MacOSX
 // pero normalmente estos sistemas operativos utilizan UTF-8 como sistema de codificación
 // por lo que el gestor de cadenas largas no es necesario.

 function NeedRTLAnsi: boolean;// true si la codificación no es UTF-8
 procedure SetNeedRTLAnsi(NewValue: boolean);
 function UTF8ToSys(const s: string): string;// como UTF8ToAnsi pero más independiente de widestringmanager
 function SysToUTF8(const s: string): string;// como AnsiToUTF8 pero mas independiente de widestringmanager

 // operaciones de fichero
 function FileExistsUTF8(const Filename: string): boolean;
 function FileAgeUTF8(const FileName: string): Longint;
 function DirectoryExistsUTF8(const Directory: string): Boolean;
 function ExpandFileNameUTF8(const FileName: string): string;
 function ExpandUNCFileNameUTF8(const FileName: string): string;
 {$IFNDEF VER2_2_0}
 function ExtractShortPathNameUTF8(Const FileName : String) : String;
 {$ENDIF}
 function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;
 function FindNextUTF8(var Rslt: TSearchRec): Longint;
 procedure FindCloseUTF8(var F: TSearchrec);
 function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;
 function FileGetAttrUTF8(const FileName: String): Longint;
 function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;
 function DeleteFileUTF8(const FileName: String): Boolean;
 function RenameFileUTF8(const OldName, NewName: String): Boolean;
 function FileSearchUTF8(const Name, DirList : String): String;
 function FileIsReadOnlyUTF8(const FileName: String): Boolean;
 function GetCurrentDirUTF8: String;
 function SetCurrentDirUTF8(const NewDir: String): Boolean;
 function CreateDirUTF8(const NewDir: String): Boolean;
 function RemoveDirUTF8(const Dir: String): Boolean;
 function ForceDirectoriesUTF8(const Dir: string): Boolean;

 // entorno
 function ParamStrUTF8(Param: Integer): string;
 function GetEnvironmentStringUTF8(Index : Integer): String;
 function GetEnvironmentVariableUTF8(const EnvVar: String): String;
 function GetAppConfigDirUTF8(Global: Boolean): string;

   Aunque la mayor parte de los parámetros pasados a estas funciones tienen unos tipos más simples otros tienen más complejidad, por eso añado aquí contenido para no tener que ir a buscar a otro sitio:    Descripción de recursos utilizados:

 function FindFirstUTF8(
   filtro:string;        // le indicamos el filtro a utilizar por ejemplo *.*, *.txt, *.exe....
   Attr: LongInt;        // atributos de búsqueda
   out Rslt: TSearchRec  // devuelve el resultado buscado con respecto a path y Attr 
 ):LongInt;              

 // Para establecer la trayectoria donde empezar a buscar se lo indicamos con SetCurrentDir()
 // por ejemplo SetCurrentDir ('c:\').
 // Para saber que trayectoria ha establecido realmente se lo podemos preguntar con GetCurrentDir
 // claro que podemos recurrir al resultado de la llamada para saber si tuvo exito pero de todos
 // modos definimos una variable string    directorio_actual:string y luego directorio_actual:=GetCurrentDir();
 //
 // Para encontrar sucesivas entradas se necesitaría utilizar FindNextUTF8.
 // Para liberar los recursos utilizados podemos emplear FindCloseUTF8.

 // Attr: LongInt donde los atributos pueden ser los siguientes:
 //
 // faReadOnly = $00000001; 
 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/fareadonly.html
 // const faHidden = $00000002;
 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/fahidden.html 
 //
 // const faSysFile = $00000004;
 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/fasysfile.html 
 // const faVolumeId = $00000008;
 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/favolumeid.html
 // const faDirectory = $00000010;
 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/fadirectory.html 
 // const faArchive = $00000020;
 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/faarchive.html
 // const faAnyFile = $0000003f;
 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/faanyfile.html
 // const faSymLink = $00000040;
 // http://community.freepascal.org:10000/docs-html/rtl/sysutils/fasymlink.html  
 // Son constantes que se pueden usar tal cual o por su valor numérico (longint)
 // como puede ser $00000008 para obtener el volumen de la unidad de disco.
 // He recogido la información basándome en FindFirst no FindFirstUTF si me equivoco
 // que me corrijan, además para sistemas Linux he encontrado esto:
 // It is not recommended to use findfirst/findnext on Linux.
 // It is an (ineffective) emulation. Instead try using the linux unit
 // and using 'Glob' instead.
 //
 // I have plans for a better findfirst/findnext algorithm on Linux,
 // but I need some time to implement it. 
 // 
 //-- Michael Van Canneyt, 18 octubre 2000 15:10  
 // De eso ya hace tiempo, yo aún no he podido practicar el equivalente en Linux
 // pero lo dejo de referencia.
 type TSearchRec = record
  Time: LongInt;  // Timestamp del fichero.
  Size: Int64;    // Tamaño del fichero.
  Attr: LongInt;  // Atributos del fichero.
  Name: TFilename; // Nombre del fichero (no parte de directorio).
  ExcludeAttr: LongInt; // Atributos a excluir de la búsqueda (no usar).
  FindHandle: Pointer; // Manejador (handle) interno del sistema operativo(no usar).
  Mode: TMode;         //Modo de fichero UNIX. Solo usado en sistemas UNIX.
  PathOnly: AnsiString; // trayecto de un fichero.
 end;   
 // donde type TFilename = String;
 //

   El siguiente es un pequeño ejemplo aplicado a un botón:    Creamos varias etiquetas (TLabel) en las que por medio de su propiedad caption visualizaremos los resultados

 procedure TForm1.Button1Click(Sender: TObject);
 var
  lista_ficheros:TSearchRec;
  directorio_actual:string;
  trayecto: string;
 begin
  lista_ficheros.name:='         ';
  SetCurrentDir('c:\');
  label1.caption:='El directorio actual es: '+GetCurrentDir();
  if findfirstutf8 ('*.*',faAnyFile,lista_ficheros) = 0 then
  begin
       if findnextutf8 (lista_ficheros) = 0 then
       label2.caption:=lista_ficheros.name;
       if findnextutf8 (lista_ficheros) = 0 then;
       label3.caption:=lista_ficheros.name;
       if findnextutf8 (lista_ficheros) = 0 then;
       label4.caption:=lista_ficheros.name;
       if findnextutf8 (lista_ficheros) = 0 then;
       label5.caption:=lista_ficheros.name;
    findcloseutf8 (lista_ficheros);
  end
  else
  label1.caption:='Hay un error en alguna parte';
 end;

Mac OS X

   Las funciones de fichero de la unidad FileUtil tienen especial cuidado en Mac OS X: OS X normaliza los nombres de fichero. Por ejemplo el nombre de fichero 'ä.txt' puede ser codificado en unicode con dos secuencias diferentes (#$C3#$A4 y 'a'#$CC#$88). Bajo Linux y BSD se puede crear un nombre de fichero con ambas codificaciones. OS X convierte automáticamente la diéresis a una secuencia de tres bytes. Esto significa:

 if Filename1=Filename2 then ... // no es suficiente bajo OS X
 if AnsiCompareFileName(Filename1,Filename2)=0 then ... // no suficiente bajo fpc 2.2.2, incluso no con cwstring
 if CompareFilenames(Filename1,Filename2)=0 then ... // esto siempre funciona (unit FileUtil o FileProcs)

UTF8 y archivos fuente - el BOM desaparecido

   Al crear archivos de código fuente con Lazarus y escribir algunos caracteres no ASCII el archivo se guarda en UTF8. No utiliza BOM (marca de orden de byte). Puedes cambiar la codificación en el menú contextual sobre el editor con la opción Ajustes de archivo... / Codificación. La razón de la omisión de BOM es cómo trata Ansistrings FPC. Por compatibilidad la LCL usa Ansistrings y para la portabilidad del LCL utiliza UTF8.

   Nota:Algunos editores de texto de MS Windows podrían tratar los archivos como con codificación del sistema y mostrar caracteres no válidos. No agregue BOM. Si se agrega BOM tenemos que cambiar todas las asignaciones de cadena.

   Por ejemplo:

 Button1.Caption:='Über';

   Cuando no hay BOM (marca de orden de byte) y no nos pasan un parámetro de página de códigos el compilador trata la cadena como sistema de codificación y copia cada byte no convertido a la cadena. Así es como el LCL espera cadenas de texto.

 // el archivo fuente está guardado como UTF sin BOM
  if FileExists('Über.txt') then ; // erróneo, porque FileExists espera codificación del sistema
  if FileExistsUTF8('Über.txt') then ; // correcto

Widestrings y Ansistrings

   al pasar de Ansistrings a Widestrings hay que convertir la codificación.

 var
  w: widestring;
  begin
   w:='Über'; // erróneo, porque FPC convertirá la codificación del sistema a UTF16
   w:=UTF8ToUTF16('Über'); // correcto
   Button1.Caption:=UTF16ToUTF8(w);
  end;

Archivos fuente con UTF8 BOM

   Si haces una gran cantidad de conversiones widestring en una unidad, el código podría ser más legible guardando el fuente como UTF8 con BOM. El compilador permite una codificación por unidad.

Lenguajes del este asiático en Windows

   La fuente por defecto (Tahoma) para los controles de interface de usuario bajo Windows XP son capaces de mostrar correctamente varios lenguajes, incluyendo árabe, ruso y lenguajes del oeste, pero no lenguajes del este tales como chino, japones y coreano.    Se puede escoger la fuente para la interfaz estándar de usuario modificando la configuración regional del sistema operativo en el panel de control, seleccionando en la solapa de lenguaje el que se necesite. De esta forma ya se podría visualizar correctamente en pantalla el lenguaje deseado.    Obviamente las versiones de XP para estos idiomas deben contener dicho paquete de lenguaje instalado. Ver instrucciones en: [1]

Guias de implementación

Requerimientos

   El espíritu de Lazarus es: "Escribe el código una vez, compila donde quieras."    Esto significa, idealmente, que una aplicación con soporte UNICODE habilitado debería tener una sola versión de código fuente soportada, sin defines condicionales en las diferentes plataformas destino.

   La parte "interface" de LCL debería soportar Unicode para las plataformas destino que las soportan ya por si mismas ocultando todas las peculiaridades para el programador de aplicaciones.

   En lo que concierne a Lazarus, la comunicación interna de cadenas en los límites "Código de aplicacion <--> LCL", así como "LCL <--> Widgetsets" está basada en las cadenas clásicas (orientadas al byte). Lógicamente, sus contenidos deberían codificarse de acuerdo a UTF-8.

Migración a UNICODE

   Las versiones anteriores de Lazarus utilizan codificación ANSI, porque era la opción por defecto para Gtk1 y win32, hasta la versión 0.9.24. Ya con la versión 0.9.25 todos los widgetsets utilizarán UTF-8 por defecto (a excepción de gtk1, que solamente soporta UTF-8 cuando el sistema lo soporta, con sus correspondientes limitaciones). Por ello todas las aplicaciones que pasen cadenas directamente al interface (tanto las escritas en código o directamente mediante el inspector de objetos) necesitarán ser convertidas a UTF-8.

   Actualmente tenemos varios grupos de widgetsets, acordes a la codificación:

  • Interfaces que utilizan codificación ANSI: gtk (1) en sistemas ANSI.
  • Interfaces que utilizan codificación UTF-8: gtk (1) en sistemas UTF-8: gtk2, qt, fpGUI, carbon, win32, wince, all others.

   Ten en cuenta que gtk 1 se encuentra en ambos grupos ANSI y UTF-8. Esto se debe a que la codificación se controla mediante una variable de entorno en Gtk 1.

   Tal como es Lazarus a fecha de hoy, el software existente funcionará si se recompila para los interfaces de win32, winCE o gtk, pero presentará complicaciones de compilación para otros widgetset. Y el nuevo software utilizando UTF-8 funcionará cuando se recompile para cualquiera de los widgesets del grupo UNICODE.

   El IDE se ha extendido para cargar(load)/salvar(save)/editar(edit) ficheros con las diferentes codificaciones (una codificación por fichero). Tiene una construcción heurística para determinar la codificación y de esta forma se puede cambiar la codificación del fichero en cualquier momento (Source Editor / Popup Menu / File Settings/ Encoding) por lo que el IDE puede abrir ficheros y proyectos antiguos y de esta forma utilizarse para convertir a la codificación deseada.

Recorrido

   Ahora que tenemos establecida la guia, es hora de crear un recorrido y ponerlo en práctica. Por ello se ha creado el siguiente plan. Dicho plan divide las tareas en dos grupos. Uno para las tareas primarias y otro para las secundarias.

   Todas las tareas primarias deben implementarse completamente antes de que se pueda decir que Lazarus tiene funcionalidad Unicode, y como tal debe prestarse especial atención a tal finalidad dedicándole el esfuerzo oportuno.

   Las tareas secundarias son deseables, pero no se implementarán a menos que algunos voluntarios se dediquen a ello o se presenten muchas peticiones.

Tareas primarias

   Hacer que Win32 Widgetset soporte UTF-8

   Notas: en este paso nos centraremos en todas las versiones de 32 bits de Windows al mismo tiemo. Todo el código generado en este esfuerzo será aislado del actual interface win32 mediante IFDEFs, para evitar introducir errores en este interface primario. Después del tiempo de transición, los IFDEFs serán removidos y únicamente se mantendrá el código UNICODE.

   Estado: totalmente implementado.

   Actualiza las funciones de teclado Gtk 2 para que trabajen con UTF-8

   Notas:

   Estado: casi completo. Algunas características pre-editadas de gtk2 no están todavía soportadas en los controles customizados. Desconozco que lenguaje necesitan.

    Hay que asegurarse de que el IDE de Lazarus funciona correctamente con el widgetset UNICODE de Win32 y que soporte UTF-8

   Notas:

   Estado: completo. Excepto por el mapa de caracteres, el cual muestra solamente 255 caracteres. De todas formas todos los sistemas operativos modernos proveen mapas de caracteres manejables.

    Hay que asegurarse de que el IDE de Lazarus funciona correctamente con el widgetset de Gtk2 y de si soporta UTF-8

   Notas:

   Estado: completo. Hay errores gtk2 intf, pero no tienen nada que ver con UTF-8.

Tareas secundarias

    Actualizar el widgetset Windows CE para que utilice UTF-8

   Notas: las rutinas de conversión de rutinas están concentrados en el fichero winceproc.pp. Se necesitan algunos tests.

   Estado: completado.

    Actualiza las funciones de teclado de Gtk 1 para que trabajen con UTF-8

   Notas:

   Estado: no implementado.

   RTL completa en synedit

   Notas: RTL significa de derecha a izquierda como por ejemplo el utilizado en árabe.

   Estado: no implementado.

Esenciales de UNICODE

   UNICODE estándar mapea enteros (integers) de 0 a 10FFFF (h) a caracteres. Cada mapeo es llamado un punto de codigo. En otras palabras, los caracteres UNICODE están en principio definidos por puntos de código desde U+000000 a U+10FFFF (0 a 1 114 111). Unicode standard maps integers from 0 to 10FFFF(h) to characters. Each such mapping is called a code point. In other words, Unicode characters are in principle defined for code points from U+000000 to U+10FFFF en hexadecimal (0 to 1.114.111 en decimal).

   Existen tres esquemas para representar punto de código UNICODE como una única secuencia de bytes. Estos esquemas se llaman formatos de transformación UNICODE: UTF-8, UTF-16 y UTF-32. La conversión entre todos ellos es posible. Aquí están sus propiedades básicas:

                            UTF-8 UTF-16 UTF-32
 Smallest code point [hex] 000000 000000 000000
 Largest code point  [hex] 10FFFF 10FFFF 10FFFF
 Code unit size [bits]          8     16     32
 Minimal bytes/character        1      2      4
 Maximal bytes/character        4      4      4

   UTF-8 tiene varias propiedades importantes y útiles: es interpretado como una secuencia de bytes, por lo que el concepto de orden bajo(lo-) y alto (hi-) no existe. Los caracteres U+0000 a U+007F (ASCII)se codifican simplemente como bytes 00h a 7Fh (por compatibilidad ASCII). Esto significa que los ficheros y cadenas que contienen solamente caracteres de 7-bit ASCII tienen la misma codificación en ambos formatos (ASCII y UTF-8). Todos los caracteres >U+007F se codifican como una secuencia de varios bytes, cada uno de los cuales tiene los dos bits más significativos puestos a 1 (bits set). No se contiene una secuencia de bytes de un carácter dentro de la larga secuencia de bytes del otro. Esto permite la búsqueda sencilla de subcadenas. El primer byte de una secuencia multibyte que representa un carácter no-ASCII está siempre en el rango C0h a FDh y esto indica cuantos bytes siguen a este carácter. Todos los caracteres que que siguen en una secuencia multibyte se encuentran en el rango 80h a BFh. Esto permite una fácil resincronización y robustez.

   UTF-16 tiene las siguientes propiedades más destacadas. Utiliza una sola palabra (words) de 16-bit para codificar caracteres desde U+0000 a U+d7ff, y un par de palabras de 16 bits para codificar cualquiera de los caracteres UNICODE restantes. remaining Unicode characters.

   Finalmente, cualquier caracter UNICODE puede ser representado como una unidad de 32 bits en UTF-32.

   Para más información, ver:

   Unicode FAQ - Basic questions,    Unicode FAQ - UTF-8, UTF-16, UTF-32 & BOM,    Wikipedia: UTF-8    [2]

Lo esencial de la arquitectura de la librería de componentes de Lazarus

   La LCL consta de dos partes:

  1. Una parte independiente de la plataforma de destino, que implementa un jerarquía de clases análoga a la VCL de Delphi;
  2. "Interfaces" - la parte que implementa la interfaz a las APIs de cada plataforma de destino.

   La comunicación entre las dos partes se realiza mediante una clase abstracta TWidgetset. Cada widgetset se implementa por su propia clase derivada de TWidgetset.

   El widgetset de GTK 1 es el más antiguo. En este widgetset se determina la codificación de los textos mediante la variable de entorno LANG, que suele ser un grupo iso ISO-8859-n de un solo byte de codificación. Recientemente (a partir de Mandriva 2007, por ejemplo), muchas distribuciones han distribuido Gtk 1 configurado para UTF-8. Nuestra interfaz Gtk 1 carece de un apoyo eficaz a UTF-8 en las rutinas de manejo del teclado, así que esto es un gran problema, que aumenta la necesidad de Lazarus de implementar soporte Unicode multiplataforma.

   El widgetset de Gtk2 únicamente funciona con codificación UTF-8 y suporta UTF-8 completamente.

   La interfaz win32 dispone de widgets ansi y el soporte UTF-8 se ha iniciado, pero no interface is setup with ansi widgets and UTF-8 support is started, pero no esta acabado y está deshabilitado. Por lo tanto, actualmente no es posible utilizar Unicode con win32.

   La interfaz Qt está preparada para UTF-8. El propio Qt usa UTF-16 como codificación nativa, pero la interfaz de Lazarus para Qt convierte de UTF-8 a UTF-16.

   Windows CE únicamente soporta UTF-16 pra la codificación de caracteres, pero nuestra interfaz para él ahora convierte las cadenas de ISO a UTF-16 antes de llamar a la API de Windows. Esto es muy fácil de solucionar, ya que todo el código de conversión se concentra en unas pocas rutinas en el archivo winceproc.pp.

   For more, see: Internals of the LCL

Habilitando UNICODE para el interface win32

Compilando LCL-Win32 con UNICODE

   Para habilitar UNICODE en LCL para Windows ir al menú, seleccionar entrada "Herramientas" --> "Configurar "Construir Lazarus..." poner dWindowsUnicodeSupport en el campo "Opciones". Selecccionar todos los objetivos a NONE, y solo LCL a Clean+Build. Selecciona win32 como objetivo wdgetset. Finalmente pulsar en "Construir".

   Ahora ya puedes recompilar tus aplicaciones pre-existentes y tendrán el modo UNICODE habilitado.

   Nota: Desde la versión 0.9.25 revisión 14883 esta operación no es necesaria debido a que Lazarus tiene el soporte UNICODE habilitado por defecto.

Directrices

   Primera, y más importante, todos los parches UNICODE para el interface Win32 deben ser incluidos por {$ifdef WindowsUnicodeSupport}, para evitar romper el interface ANSI existente. Cuándo el código sea estable, todos los ifdef se quitaran y solamente permanecerá la parte UNICODE. En este momento todos los programas existentes que usan caracteres ANSI necesitaran migrar a UNICODE.

   Las plantaformas windows <=Win9x se basan en códigos de página de la norma ISO y soportan Unicode de forma parcial. Las plataformas win a partir de WinNT y Windows CE suportan completamente UNICODE. Win 9x y NT ofrecen dos conjuntos paralelos de funciones API: el antiguo ANSI demoninado *A y el nuevo, UNICODE demonimado *W. Las funciones *W aceptan wide strings, por ejemplo cadenas codificadas UTF-16 como parámetros. Windows CE solamente utiliza funciones Wide API.

Funciones Wide presentes en Windows 9x

   Algunas funciones Wide API están presentes en Windows 9x. La siguiente es una lista de las mismas: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/other_existing_unicode_support.asp

   Ejemplo de conversión:

 GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption),
 Length(ButtonCaption), TextSize);

   Viene a ser:

 {$ifdef WindowsUnicodeSupport}
    GetTextExtentPoint32W(hdcNewBitmap, PWideChar(Utf8Decode(ButtonCaption)), Length(WideCaption), TextSize);
  {$else}
    GetTextExtentPoint32(hdcNewBitmap, LPSTR(ButtonCaption), Length(ButtonCaption), TextSize);
  {$endif}

Funciones que necesitan versiones ANSI y Wide

   Primer ejemplo de conversión:

 function TGDIWindow.GetTitle: String;
 var
  l: Integer;
 begin
   l := Windows.GetWindowTextLength(Handle);
   SetLength(Result, l);
   Windows.GetWindowText(Handle, @Result[1], l);
 end;

   Viene a ser así:

 function TGDIWindow.GetTitle: String;
 var
  l: Integer;
  AnsiBuffer: string;
  WideBuffer: WideString;
 begin

 {$ifdef WindowsUnicodeSupport}

  if UnicodeEnabledOS then
   begin
    l := Windows.GetWindowTextLengthW(Handle);
    SetLength(WideBuffer, l);
    l := Windows.GetWindowTextW(Handle, @WideBuffer[1], l);
    SetLength(WideBuffer, l);
    Result := Utf8Encode(WideBuffer);
   end
  else
   begin
    l := Windows.GetWindowTextLength(Handle);
    SetLength(AnsiBuffer, l);
    l := Windows.GetWindowText(Handle, @AnsiBuffer[1], l);
    SetLength(AnsiBuffer, l);
    Result := AnsiToUtf8(AnsiBuffer);
  end;

{$else}

  l := Windows.GetWindowTextLength(Handle);
  SetLength(Result, l);
  Windows.GetWindowText(Handle, @Result[1], l);

{$endif}

 end;

Recorrido

Que debería estar ya funcionando con UNICODE:

  • TForm, TButton, TLabel.
  • La mayor parte de los controles.
  • Menús.
  • LCLIntf.ExtTextOut y muchas otras winapis de texto relacionadas.
  • Controles basados en TStrings. Ejemplos: TComboBox, TListBox, etc
  • SynEdit muestra y permite la introducción de caracteres UTF-8 correctamente.
  • Estableciendo(setting)/Obteniendo(Getting) cadenas UNICODE hacia/desde el portapapeles (ClipBoard).
  • Estableciendo el título de la aplicación en las opciones de proyecto a (por ejemplo) 'Mi aplicación'.
  • Haciendo doble ckick en palabras con caracteres no-ascii en el editor para seleccionarlos.

Problemas conocidos con el soporte UNICODE

  • SynEdit no soporta RTL de derecha a izquierda (Right To Left)
  • ¿Ha sido OpenFileDialogCallBack testeado con un largo número de ficheros?
    • ¿Es esto un problema específico de UNICODE? Yo creo que es un problema genérico. --Sekelsenmat 13:40, 14 February 2008 (CET)
      • Puede ser. Lo he testeado con una gran cantidad de ficheros antes de añadir la versión UNICODE. Si se trata de un problema genérico, entonces la versión no-UNICODE estaría rota cuando la versión unicode fue añadida. Vincent 21:45, 15 February 2008 (CET)
  • Función de clase TWin32WSSelectDirectoryDialog.CreateHandle: Title, nombre de fichero(FileName) y directorio inicial (InitialDir) debería ser convertido a UNICODE.

   Posibles problemas con el soporte UNICODE

   Basandose en la revisión de código, lo siguiente necesita ser testeado porque el código parece no estar preparado para UNICODE:

  • Procedimiento de clase TWin32WSCustomComboBox.SetText
  • TWin32WSCustomTrayIcon.Show: ATrayIcon.Hint no soporta UNICODE
  • TWin32WidgetSet.MessageBox doesn't call MessageBoxW.
  • TWin32WidgetSet.TextOut: es Windows.TextOut soportado en windows 9X?
  • MessageBox buttons no muestran el UNICODE cuando son traducidos. Testeados en el IDE. Puede haber un problema en el IDE.
    • Nota: no he podido reproducirlo utilizando la traducción portuguesa. --Sekelsenmat 22:20, 12 January 2008 (CET)
  • (lista de problemas no confirmados, si se confirman pueden ser movidos al listado de arriba)

Pantallazos

   Lazarus Unicode Test.png

Ver también

  • UTF-8 - Descripción de cadenas UTF-8.