unit FarsiReshaper;
interface
uses System.Classes, System.Math;
type
TStruc = class sealed (TObject)
public
Character,
EndGlyph,
IniGlyph,
MidGlyph,
IsoGlyph: Char;
constructor Create(ACharacter, AEndGlyph, AIniGlyph, AMidGlyph, AIsoGlyph: Char);
end;
TFarsi = class sealed (TObject)
strict private
const N_DISTINCT_CHARACTERS = 43;
class var SzLamAndAlef: String;
class var SzLamStickAndAlef: String;
class var SzLa: String;
class var SzLaStick: String;
class var SzLamAndAlefWoosim: String;
class var SzLamStickAndAlefWoosim: String;
class var SzLaWoosim: String;
class var SzLaStickWoosim: String;
class var ArrStruc: array of TStruc;
class var ArrStrucWoosim: array of TStruc;
class var IsFarsiConversionNeeded : Boolean;
class function IsFarsiChar(Ch: Char): Boolean;
class function FarsiReverse(S: String): String;
class function IsFromTheSet1(Ch: Char): Boolean;
class function IsFromTheSet2(Ch: Char): Boolean;
class function CharIsLTR(Ch: Char): Boolean;
class function CharIsRTL(Ch: Char): Boolean;
class function ReorderWords(S: String): String;
class function ConvertWord(S: String): String;
public
class function Convert(S: String): String;
class function ConvertBackToRealFarsi(S: String): String;
class constructor Create;
end;
implementation
uses
System.StrUtils, System.SysUtils, System.Character;
{ TStruc }
constructor TStruc.Create(ACharacter, AEndGlyph, AIniGlyph, AMidGlyph, AIsoGlyph: Char);
begin
Character:= ACharacter;
EndGlyph:= AEndGlyph;
IniGlyph:= AIniGlyph;
MidGlyph:= AMidGlyph;
IsoGlyph:= AIsoGlyph;
end;
{ TFarsi }
class function TFarsi.FarsiReverse(S: String): String;
var
LRev: String;
I: Integer;
begin
Result:= EmptyStr;
LRev:= EmptyStr;
S:= ReverseString(S);
I:= 0;
while (I < S.Length) do
begin
if S.Chars[I].IsDigit then
begin
LRev:= EmptyStr;
while (I < S.Length) and (S.Chars[I].IsDigit or (S.Chars[I] = '/') or (S.Chars[I] = '.')) do
begin
LRev:= LRev + S.Chars[I];
Inc(I);
end;
LRev:= ReverseString(LRev);
Result:= Result + LRev;
end
else
begin
Result:= Result + S.Chars[I];
Inc(I);
end;
end;
end;
class function TFarsi.CharIsLTR(Ch: Char): Boolean;
begin
Result:= ((Ch >= Char(65)) and (Ch <= Char(122))) or Ch.IsDigit;
end;
class function TFarsi.CharIsRTL(Ch: Char): Boolean;
begin
Result:= (Ch >= Char($0621))
or
(Ch = Char($060C)) // ،
or
(Ch = Char($061B)) // ؛
or
(Ch = Char($061F)) // ؟
or
((Ch >= Char($0021)) and (Ch <= Char($002F)))
or
((Ch >= Char($003A)) and (Ch <= Char($003F)))
or
(Ch = Char($005B))
or
(Ch = Char($005D))
or
(Ch = Char($007B))
or
(Ch = Char($007D));
end;
class function TFarsi.ReorderWords(S: String): String;
const
ST_RTL = 0;
ST_LTR = 1;
var
LPrevWord: String;
LState, LPreState, I: Integer;
begin
Result:= EmptyStr;
LPrevWord:= EmptyStr;
LState:= ST_RTL;
LPreState:= ST_RTL;
I:= 0;
while (I < S.Length) do
begin
if CharIsLTR(S.Chars[I]) and (LState <> ST_LTR) then
begin
// State changed to LTR
LPreState:= ST_RTL;
LState:= ST_LTR;
Result:= Result + LPrevWord;
LPrevWord:= S.Chars[I];
end
else
if CharIsRTL(S.Chars[I]) and (LState <> ST_RTL) then
begin
// State changed to RTL
LPreState:= ST_LTR;
LState:= ST_RTL;
Result:= LPrevWord + Result;
LPrevWord:= S.Chars[I];
end
else
// State is not changed
begin
case LState of
ST_RTL: LPrevWord:= S.Chars[I] + LPrevWord;
ST_LTR: LPrevWord:= LPrevWord + S.Chars[I];
end;
//LPrevWord:= LPrevWord + S.Chars[I];
end;
Inc(I);
end;
// Result:= LPrevWord + Result;
case LPreState of
ST_RTL: Result:= LPrevWord + Result;
ST_LTR: Result:= Result + LPrevWord;
end;
end;
class function TFarsi.ConvertWord(S: String): String;
var
LLinkBefore, LLinkAfter: Boolean;
LIdx, I: Integer;
LChr: Char;
begin
Result:= DupeString(' ', S.Length);
LLinkBefore:= False;
LLinkAfter:= False;
I:= 0;
LIdx:= 0;
if (not TFarsi.IsFarsiConversionNeeded) or (S.IsEmpty) then
Exit(S);
while (I < S.Length) do
begin
if IsFarsiChar(S.Chars[I]) then
begin
LIdx:= 0;
LChr:= #0;
while (LIdx < N_DISTINCT_CHARACTERS) do
begin
if ArrStruc[LIdx].Character = S.Chars[I] then
Break;
Inc(LIdx);
end;
if (I = S.Length - 1) then
LLinkAfter:= False
else
LLinkAfter:= IsFromTheSet1(S.Chars[I + 1]) or IsFromTheSet2(S.Chars[I + 1]);
if I = 0 then
LLinkBefore:= False
else
LLinkBefore:= IsFromTheSet1(S.Chars[I - 1]);
if (LIdx < N_DISTINCT_CHARACTERS) then
begin
if LLinkBefore and LLinkAfter then
LChr:= ArrStruc[LIdx].MidGlyph
else
if LLinkBefore and not LLinkAfter then
LChr:= ArrStruc[LIdx].EndGlyph
else
if not LLinkBefore and LLinkAfter then
LChr:= ArrStruc[LIdx].IniGlyph
else
if not LLinkBefore and not LLinkAfter then
LChr:= ArrStruc[LIdx].IsoGlyph;
end
else
LChr:= S.Chars[I];
Result[I]:= LChr;
end
else
Result[I]:= S.Chars[I];
Inc(I);
end;
Result:= Result.Replace(Char($200c), ' '); // Change NO SPACE to SPACE
Result:= Result.Replace(szLamAndAlef, szLa); // Join 'Lam' and 'Alef' and make 'La'
Result:= Result.Replace(szLamStickAndAlef, szLaStick); // Join 'Lam Stick' and 'Alef' and make 'La Stick'
Result:= ReorderWords(Result);
end;
class function TFarsi.Convert(S: String): String;
var
LArrWords: TArray<String>;
I: Integer;
begin
// Result:= ConvertWord(S);
Result:= EmptyStr;
LArrWords:= S.Split([' ']);
for I := Low(LArrWords) to High(LArrWords) do
begin
LArrWords[I]:= ConvertWord(LArrWords[I]);
Result:= LArrWords[I] + ' ' + Result;
end;
end;
class function TFarsi.ConvertBackToRealFarsi(S: String): String;
var
LSB: TStringBuilder;
I, J: Integer;
LFound: Boolean;
begin
Result:= EmptyStr;
I:= 0;
J:= 0;
if not IsFarsiConversionNeeded then
Exit(S);
LSB:= TStringBuilder.Create(EmptyStr);
try
while (I < S.Length) do
begin
LFound:= False;
for J := Low(ArrStruc) to High(ArrStruc) do
begin
if (S.Chars[I] = ArrStruc[J].MidGlyph)
or
(S.Chars[I] = ArrStruc[J].IniGlyph)
or
(S.Chars[I] = ArrStruc[J].EndGlyph)
or
(S.Chars[I] = ArrStruc[J].IsoGlyph) then
begin
LSB.Append(ArrStruc[J].Character);
LFound:= True;
Break;
end;
end;
if not LFound then
LSB.Append(S.Chars[I]);
Inc(I);
end;
Result:= LSB.ToString;
Result:= Result.Replace(TFarsi.SzLa, 'لا');
Result:= Result.Replace(TFarsi.SzLaStick, 'لا');
//Result:= TFarsi.ReorderWords(Result);
finally
FreeAndNil(LSB);
end;
end;
class constructor TFarsi.Create;
begin
TFarsi.IsFarsiConversionNeeded:= True;
TFarsi.SzLamAndAlef := Char($FEDF) + Char($FE8E); // Lam + Alef
TFarsi.SzLamStickAndAlef := Char($FEE0) + Char($FE8E); // Lam (Sticky !!!)+
TFarsi.SzLa := Char($FEFB); // La
TFarsi.SzLaStick := Char($FEFC); // La (Sticky!!!)
TFarsi.SzLamAndAlefWoosim := Char($E1) + Char($BB); // Lam + Alef
TFarsi.SzLamStickAndAlefWoosim := Char($90) + Char($BB); // Lam (Sticky !!!)+ Alef
TFarsi.SzLaWoosim := Char($D9); // La
TFarsi.SzLaStickWoosim := Char($D9); // La
{Array}
TFarsi.ArrStruc:=
[
TStruc.Create(Char($630), Char($FEAC), Char($FEAB), Char($FEAC), Char($FEAB)),
TStruc.Create(Char($62F), Char($FEAA), Char($FEA9), Char($FEAA), Char($FEA9)),
TStruc.Create(Char($62C), Char($FE9E), Char($FE9F), Char($FEA0), Char($FE9D)),
TStruc.Create(Char($62D), Char($FEA2), Char($FEA3), Char($FEA4), Char($FEA1)),
TStruc.Create(Char($62E), Char($FEA6), Char($FEA7), Char($FEA8), Char($FEA5)),
TStruc.Create(Char($647), Char($FEEA), Char($FEEB), Char($FEEC), Char($FEE9)),
TStruc.Create(Char($639), Char($FECA), Char($FECB), Char($FECC), Char($FEC9)),
TStruc.Create(Char($63A), Char($FECE), Char($FECF), Char($FED0), Char($FECD)),
TStruc.Create(Char($641), Char($FED2), Char($FED3), Char($FED4), Char($FED1)),
TStruc.Create(Char($642), Char($FED6), Char($FED7), Char($FED8), Char($FED5)),
TStruc.Create(Char($62B), Char($FE9A), Char($FE9B), Char($FE9C), Char($FE99)),
TStruc.Create(Char($635), Char($FEBA), Char($FEBB), Char($FEBC), Char($FEB9)),
TStruc.Create(Char($636), Char($FEBE), Char($FEBF), Char($FEC0), Char($FEBD)),
TStruc.Create(Char($637), Char($FEC2), Char($FEC3), Char($FEC4), Char($FEC1)),
TStruc.Create(Char($643), Char($FEDA), Char($FEDB), Char($FEDC), Char($FED9)),
TStruc.Create(Char($645), Char($FEE2), Char($FEE3), Char($FEE4), Char($FEE1)),
TStruc.Create(Char($646), Char($FEE6), Char($FEE7), Char($FEE8), Char($FEE5)),
TStruc.Create(Char($62A), Char($FE96), Char($FE97), Char($FE98), Char($FE95)),
TStruc.Create(Char($627), Char($FE8E), Char($FE8D), Char($FE8E), Char($FE8D)),
TStruc.Create(Char($644), Char($FEDE), Char($FEDF), Char($FEE0), Char($FEDD)),
TStruc.Create(Char($628), Char($FE90), Char($FE91), Char($FE92), Char($FE8F)),
TStruc.Create(Char($64A), Char($FEF2), Char($FEF3), Char($FEF4), Char($FEF1)),
TStruc.Create(Char($633), Char($FEB2), Char($FEB3), Char($FEB4), Char($FEB1)),
TStruc.Create(Char($634), Char($FEB6), Char($FEB7), Char($FEB8), Char($FEB5)),
TStruc.Create(Char($638), Char($FEC6), Char($FEC7), Char($FEC8), Char($FEC5)),
TStruc.Create(Char($632), Char($FEB0), Char($FEAF), Char($FEB0), Char($FEAF)),
TStruc.Create(Char($648), Char($FEEE), Char($FEED), Char($FEEE), Char($FEED)),
TStruc.Create(Char($629), Char($FE94), Char($FE93), Char($FE93), Char($FE93)),
TStruc.Create(Char($649), Char($FEF0), Char($FEEF), Char($FEF0), Char($FEEF)),
TStruc.Create(Char($631), Char($FEAE), Char($FEAD), Char($FEAE), Char($FEAD)),
TStruc.Create(Char($624), Char($FE86), Char($FE85), Char($FE86), Char($FE85)),
TStruc.Create(Char($621), Char($FE80), Char($FE80), Char($FE80), Char($FE80)),
TStruc.Create(Char($626), Char($FE8A), Char($FE8B), Char($FE8C), Char($FE89)),
TStruc.Create(Char($623), Char($FE84), Char($FE83), Char($FE84), Char($FE83)),
TStruc.Create(Char($622), Char($FE82), Char($FE81), Char($FE82), Char($FE81)),
TStruc.Create(Char($625), Char($FE88), Char($FE87), Char($FE88), Char($FE87)),
TStruc.Create(Char($67E), Char($FB57), Char($FB58), Char($FB59), Char($FB56)),
TStruc.Create(Char($686), Char($FB7B), Char($FB7C), Char($FB7D), Char($FB7A)),
TStruc.Create(Char($698), Char($FB8B), Char($FB8A), Char($FB8B), Char($FB8A)),
TStruc.Create(Char($6A9), Char($FB8F), Char($FB90), Char($FB91), Char($FB8E)),
TStruc.Create(Char($6AF), Char($FB93), Char($FB94), Char($FB95), Char($FB92)),
TStruc.Create(Char($6CC), Char($FBFD), Char($FEF3), Char($FEF4), Char($FBFC)),
TStruc.Create(Char($6C0), Char($FBA5), Char($FBA4), Char($FBA5), Char($FBA4))
];
TFarsi.ArrStrucWoosim:=
[
TStruc.Create(Char($630), Char($B5), Char($82), Char($B5), Char($82)),
TStruc.Create(Char($62F), Char($B4), Char($81), Char($B4), Char($81)),
TStruc.Create(Char($62C), Char($9B), Char($B1), Char($F9), Char($BF)),
TStruc.Create(Char($62D), Char($9C), Char($B2), Char($FA), Char($C0)),
TStruc.Create(Char($62E), Char($9D), Char($B3), Char($FE), Char($C1)),
TStruc.Create(Char($647), Char($AC), Char($E4), Char($93), Char($D5)),
TStruc.Create(Char($639), Char($C9), Char($D3), Char($8B), Char($A4)),
TStruc.Create(Char($63A), Char($CA), Char($DD), Char($8C), Char($A5)),
TStruc.Create(Char($641), Char($A6), Char($DE), Char($8D), Char($CC)),
TStruc.Create(Char($642), Char($A7), Char($DF), Char($8E), Char($CE)),
TStruc.Create(Char($62B), Char($BD), Char($AF), Char($EA), Char($99)),
TStruc.Create(Char($635), Char($C4), Char($C8), Char($87), Char($A0)),
TStruc.Create(Char($636), Char($C5), Char($CB), Char($88), Char($A1)),
TStruc.Create(Char($637), Char($C6), Char($CD), Char($CD), Char($A2)),
TStruc.Create(Char($643), Char($CF), Char($E0), Char($8F), Char($A8)),
TStruc.Create(Char($645), Char($D2), Char($E2), Char($91), Char($AA)),
TStruc.Create(Char($646), Char($D4), Char($E3), Char($92), Char($AB)),
TStruc.Create(Char($62A), Char($BD), Char($AF), Char($EA), Char($99)),
TStruc.Create(Char($627), Char($BB), Char($80), Char($BB), Char($80)),
TStruc.Create(Char($644), Char($D1), Char($E1), Char($90), Char($A9)),
TStruc.Create(Char($628), Char($BC), Char($AE), Char($E9), Char($98)),
TStruc.Create(Char($64A), Char($DC), Char($E6), Char($95), Char($DC)),
TStruc.Create(Char($633), Char($C2), Char($B8), Char($B8), Char($9E)),
TStruc.Create(Char($634), Char($C3), Char($B9), Char($B9), Char($9F)),
TStruc.Create(Char($638), Char($C7), Char($CD), Char($CD), Char($C7)),
TStruc.Create(Char($632), Char($B7), Char($B7), Char($B7), Char($B7)),
TStruc.Create(Char($648), Char($94), Char($94), Char($94), Char($94)),
TStruc.Create(Char($629), Char($DA), Char($DA), Char($DA), Char($DA)),
TStruc.Create(Char($649), Char($DC), Char($E6), Char($95), Char($DC)),
TStruc.Create(Char($631), Char($B6), Char($B6), Char($B6), Char($B6)),
TStruc.Create(Char($624), Char($E7), Char($E7), Char($E7), Char($E7)),
TStruc.Create(Char($621), Char($BA), Char($BA), Char($BA), Char($BA)),
TStruc.Create(Char($626), Char($D7), Char($E8), Char($97), Char($D7)),
TStruc.Create(Char($623), Char($80), Char($80), Char($80), Char($80)),
TStruc.Create(Char($622), Char($80), Char($80), Char($80), Char($80)),
TStruc.Create(Char($625), Char($80), Char($80), Char($80), Char($80)),
TStruc.Create(Char($67E), Char($BC), Char($AE), Char($E9), Char($98)),
TStruc.Create(Char($686), Char($9B), Char($B1), Char($F9), Char($BF)),
TStruc.Create(Char($698), Char($B7), Char($B7), Char($B7), Char($B7)),
TStruc.Create(Char($6A9), Char($CF), Char($E0), Char($8F), Char($A8)),
TStruc.Create(Char($6AF), Char($CF), Char($E0), Char($8F), Char($A8)),
TStruc.Create(Char($6CC), Char($DC), Char($E6), Char($95), Char($DC)),
TStruc.Create(Char($6C0), Char($AC), Char($E4), Char($93), Char($D5))
];
end;
class function TFarsi.IsFarsiChar(Ch: Char): Boolean;
begin
Result:= ((Ch >= Char($0621)) and (Ch <= Char($064a)))
or
(Ch = Char($067e))
or
(Ch = Char($0686))
or
(Ch = Char($0698))
or
(Ch = Char($06a9))
or
(Ch = Char($06af))
or
(Ch = Char($06cc))
or
(Ch = Char($06c0));
end;
class function TFarsi.IsFromTheSet1(Ch: Char): Boolean;
var
LTheSet1: array of Char;
I: Integer;
begin
Result:= False;
I:= 0;
LTheSet1:= [
Char($62C), Char($62D), Char($62E), Char($647), Char($639), Char($63A),
Char($641), Char($642), Char($62B), Char($635), Char($636), Char($637),
Char($643), Char($645), Char($646), Char($62A), Char($644), Char($628),
Char($64A), Char($633), Char($634), Char($638), Char($67E), Char($686),
Char($6A9), Char($6AF), Char($6CC), Char($626)
];
while (I < 28) do
begin
if Ch = LTheSet1[I] then
Exit(True);
Inc(I);
end;
end;
class function TFarsi.IsFromTheSet2(Ch: Char): Boolean;
var
LTheSet2: array of Char;
I: Integer;
begin
Result:= False;
I:= 0;
LTheSet2:= [
Char($627), Char($623), Char($625), Char($622), Char($62F), Char($630),
Char($631), Char($632), Char($648), Char($624), Char($629), Char($649),
Char($698), Char($6C0)
];
while (I < 14) do
begin
if Ch = LTheSet2[I] then
Exit(True);
Inc(I);
end;
end;
end.