freebidi.pp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. {
  2. Author Mazen NEIFER
  3. Licence LGPL
  4. }
  5. unit FreeBIDI;
  6. {$mode objfpc}{$H+}
  7. interface
  8. type
  9. TCharacter = WideChar;
  10. TString = WideString;
  11. TDirection=(
  12. drNONE,
  13. drRTL,
  14. drLTR
  15. );
  16. TVisualToLogical = Array[Byte]Of Byte;
  17. TFontInfoPtr = Pointer;
  18. TCharWidthRoutine = function(Character:TCharacter;FontInfo:TFontInfoPtr):Integer;
  19. var
  20. FontInfoPtr:TFontInfoPtr;
  21. CharWidth:TCharWidthRoutine;
  22. {****************************Logical aspects***********************************}
  23. {Returns the number of logical characters}
  24. function LLength(const Src:TString):Cardinal;
  25. {Converts visual position to logical position}
  26. function LPos(const Src:TString; vp:Integer; pDir:TDirection):Cardinal;
  27. {****************************Visual aspects************************************}
  28. {Returns the number of visual characters}
  29. function VLength(const Src:TString; pDir:TDirection):Cardinal;
  30. {Converts a logical position to a visual position}
  31. function VPos(const Src:TString; lp:Integer; pDir, cDir:TDirection):Cardinal;
  32. function VPos(UTF8Char:PChar; Len:integer; BytePos:integer):Cardinal;
  33. {Returns character at a given visual position according to paragraph direction}
  34. function VCharOf(Src:TString; vp:Integer; dir:TDirection):TCharacter;
  35. {Inserts a string into an other paying attention of RTL/LTR direction}
  36. procedure VInsert(const Src:TString; var Dest:TString; vp:Integer; pDir:TDirection);
  37. {Deletes a string into an other paying attention of RTL/LTR direction}
  38. procedure VDelete(var str:TString; vp, len:Integer; pDir:TDirection);
  39. {Resturns a sub string of source string}
  40. //function VCopy(const Src:TString; vStart, vWidth:Integer):TString;
  41. {Resturns the visual image of current string}
  42. function VStr(const Src:TString; pDir:TDirection):TString;
  43. {****************************Helper routines***********************************}
  44. {Returns direction of a character}
  45. function DirectionOf(Character:TCharacter):TDirection;
  46. {Returns contextual direction of caracter in a string}
  47. function DirectionOf(Src:TString; lp:Integer; pDir:TDirection):TDirection;
  48. {Inserts a char as if it was typed using keyboard in the most user friendly way.
  49. Returns the new cursor position after insersion depending on the new visual text}
  50. function InsertChar(Src:TCharacter; var Dest:TString; vp:Integer; pDir:TDirection):Integer;
  51. {Returns a table mapping each visual position to its logical position in an UTF8*
  52. string}
  53. function VisualToLogical(const Src:TString; pDir:TDirection):TVisualToLogical;
  54. implementation
  55. function DefaultCharWidth(Character:TCharacter; FontInfoPtr:TFontInfoPtr):Integer;
  56. begin
  57. case Character of
  58. #9:
  59. Result := 8;
  60. else
  61. Result := 1;
  62. end;
  63. end;
  64. function DumpStr(const Src:TString):String;
  65. var
  66. i:Integer;
  67. begin
  68. Result := '';
  69. for i:= 1 to Length(Src) do
  70. case Src[i] of
  71. #0..#127:
  72. Result := Result + Src[i];
  73. else
  74. Result := Result + '$' + HexStr(Ord(Src[i]),4);
  75. end;
  76. end;
  77. function ComputeCharLength(p:PChar):Cardinal;
  78. begin
  79. if ord(p^)<%11000000
  80. then
  81. {regular single byte character (#0 is a normal char, this is UTF8Charascal ;)}
  82. Result:=1
  83. else if ((ord(p^) and %11100000) = %11000000)
  84. then
  85. if (ord(p[1]) and %11000000) = %10000000 then
  86. Result:=2
  87. else
  88. Result:=1
  89. else if ((ord(p^) and %11110000) = %11100000)
  90. then
  91. if ((ord(p[1]) and %11000000) = %10000000)
  92. and ((ord(p[2]) and %11000000) = %10000000)
  93. then
  94. Result:=3
  95. else
  96. Result:=1
  97. else if ((ord(p^) and %11111000) = %11110000)
  98. then
  99. if ((ord(p[1]) and %11000000) = %10000000)
  100. and ((ord(p[2]) and %11000000) = %10000000)
  101. and ((ord(p[3]) and %11000000) = %10000000)
  102. then
  103. Result:=4
  104. else
  105. Result:=1
  106. else
  107. Result:=1
  108. end;
  109. {****************************Logical aspects***********************************}
  110. function LLength(const Src:TString):Cardinal;
  111. begin
  112. Result := Length(Src);
  113. end;
  114. function LPos(const Src:TString; vp:Integer; pDir:TDirection):Cardinal;
  115. var
  116. v2l:TVisualToLogical;
  117. i:integer;
  118. begin
  119. v2l := VisualToLogical(Src, pDir);
  120. if vp <= v2l[0]
  121. then
  122. Result := v2l[vp]
  123. else
  124. Result := Length(Src) + 1;
  125. end;
  126. {****************************Visual aspects************************************}
  127. function VLength(const Src:TString; pDir:TDirection):Cardinal;
  128. var
  129. Count:Integer;
  130. begin
  131. Result := 0;
  132. Count := Length(Src);
  133. while (Count > 0) do
  134. begin
  135. Result += CharWidth(Src[Count], FontInfoPtr);
  136. Count -= 1;
  137. end;
  138. end;
  139. function VPos(const Src:TString; lp:Integer; pDir, cDir:TDirection):Cardinal;
  140. var
  141. v2l:TVisualToLogical;
  142. vp:Integer;
  143. begin
  144. v2l := VisualToLogical(Src, pDir);
  145. for vp := 1 to v2l[0] do
  146. if lp = v2l[vp]
  147. then
  148. begin
  149. Exit(vp);
  150. end;
  151. Result := v2l[0];
  152. end;
  153. function VPos(UTF8Char:PChar; Len:integer; BytePos:integer):Cardinal;
  154. begin
  155. end;
  156. function VCharOf(Src:TString; vp:Integer; dir:TDirection):TCharacter;
  157. var
  158. CharLen: LongInt;
  159. begin
  160. Result := Src[LPos(Src, vp, dir)];
  161. end;
  162. {****************************Helper routines***********************************}
  163. function DirectionOf(Character:TCharacter):TDirection;
  164. begin
  165. case Character of
  166. #9,#32,
  167. '/',
  168. '{','}',
  169. '[',']',
  170. '(',')':
  171. Result := drNONE;
  172. #$0590..#$05FF, //Hebrew
  173. #$0600..#$06FF: //Arabic
  174. Result := drRTL;
  175. else
  176. Result := drLTR;
  177. end;
  178. end;
  179. function DirectionOf(Src:TString; lp:Integer; pDir:TDirection):TDirection;
  180. var
  181. c:TCharacter;
  182. lDir,rDir:TDirection;
  183. p:Integer;
  184. begin
  185. if(lp <= 0)
  186. then
  187. lp := 1;
  188. {Seek for proper character direction}
  189. c := Src[lp];
  190. lDir := DirectionOf(c);
  191. {Seek for left character direction if it is neutral}
  192. p := lp;
  193. while(p > 1) and (lDir = drNONE)do
  194. begin
  195. c := Src[p - 1];
  196. lDir := DirectionOf(c);
  197. p := p - Length(c);
  198. end;
  199. {Seek for right character direction if it is neutral}
  200. p := lp;
  201. repeat
  202. c := Src[p];
  203. rDir := DirectionOf(c);
  204. p := p + Length(c);
  205. until(p > Length(Src)) or (rDir <> drNONE);
  206. if(lDir = rDir)
  207. then
  208. Result := rDir
  209. else
  210. Result := pDir;
  211. end;
  212. function VisualToLogical(const Src:TString; pDir:TDirection):TVisualToLogical;
  213. procedure Insert(value:Byte; var v2l:TVisualToLogical; InsPos:Byte);
  214. var
  215. l:Byte;
  216. begin
  217. if v2l[0] < 255
  218. then
  219. Inc(InsPos);
  220. if InsPos > v2l[0]
  221. then
  222. InsPos := v2l[0];
  223. for l := v2l[0] downto InsPos do
  224. v2l[l] := v2l[l-1];
  225. v2l[InsPos] := Value;
  226. end;
  227. var
  228. lp, vp : Integer;
  229. cDir,lDir:TDirection;
  230. Character:TCharacter;
  231. i:Integer;
  232. begin
  233. Result[0] := 0;
  234. lp := 1;
  235. vp := 1;
  236. lDir := drNONE;
  237. while lp <= Length(Src) do
  238. begin
  239. Character := Src[lp];
  240. cDir := DirectionOf(Src, lp, pDir);
  241. Inc(Result[0]);
  242. case cDir of
  243. drRTL:
  244. begin
  245. lDir := drRTL;
  246. end;
  247. drLTR:
  248. begin
  249. lDir := drLTR;
  250. vp := Result[0];
  251. end;
  252. else
  253. vp := Result[0];
  254. end;
  255. Insert(lp, Result, vp);
  256. lp += 1;
  257. end;
  258. end;
  259. function InsertChar(Src:TCharacter; var Dest:TString; vp:Integer; pDir:TDirection):Integer;
  260. var
  261. vSrc,vDest:TString;
  262. begin
  263. vSrc := VStr(Src,pDir);
  264. vDest := VStr(Dest,pDir);
  265. Insert(vSrc, vDest, vp);
  266. Dest := VStr(vDest, pDir);
  267. case DirectionOf(Src) of
  268. drRTL:
  269. Result := vp;
  270. drLTR:
  271. Result := vp + 1;
  272. else
  273. if(vp < Length(vDest)) and (DirectionOf(vDest[vp + 1]) = drRTL)
  274. then
  275. Result := vp
  276. else
  277. Result := vp + 1;
  278. end;
  279. end;
  280. procedure VInsert(const Src:TString;var Dest:TString; vp:Integer; pDir:TDirection);
  281. var
  282. vSrc,vDest:TString;
  283. begin
  284. vSrc := VStr(Src,pDir);
  285. vDest := VStr(Dest,pDir);
  286. Insert(vSrc, vDest, vp);
  287. Dest := VStr(vDest, pDir);
  288. end;
  289. procedure VDelete(var str:TString; vp, len:Integer; pDir:TDirection);
  290. var
  291. v2l:TVisualToLogical;
  292. i:Integer;
  293. begin
  294. v2l := VisualToLogical(str, pDir);
  295. for i := 1 to v2l[0] do
  296. if(v2l[i] >= vp) and (v2l[i] < vp + len)
  297. then
  298. Delete(str, v2l[i], 1);
  299. end;
  300. function VStr(const Src:TString; pDir:TDirection):TString;
  301. var
  302. v2lSrc:TVisualToLogical;
  303. vp:Integer;
  304. begin
  305. v2lSrc := VisualToLogical(Src,pDir);
  306. SetLength(Result, v2lSrc[0]);
  307. for vp := 1 to v2lSrc[0] do
  308. Result[vp] := Src[v2lSrc[vp]];
  309. end;
  310. initialization
  311. CharWidth := @DefaultCharWidth;
  312. end.