unihelper.lpr 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. { Unicode tables parser.
  2. Copyright (c) 2012 by Inoussa OUEDRAOGO
  3. The source code is distributed under the Library GNU
  4. General Public License with the following modification:
  5. - object files and libraries linked into an application may be
  6. distributed without source code.
  7. If you didn't receive a copy of the file COPYING, contact:
  8. Free Software Foundation
  9. 675 Mass Ave
  10. Cambridge, MA 02139
  11. USA
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. }
  15. { This program generates tables as include-files for use
  16. with the unicode related sources. It expects the following
  17. unicode.org's files to be present in the same folder :
  18. * HangulSyllableType.txt
  19. * PropList.txt
  20. * UnicodeData.txt
  21. * allkeys.txt
  22. }
  23. {$DEFINE UCA_TEST}
  24. program unihelper;
  25. {$mode objfpc}{$H+}
  26. {$typedaddress on}
  27. uses
  28. SysUtils, Classes,
  29. helper, uca_test;
  30. const
  31. SUsage =
  32. 'This program generates tables as include-files for use ' + sLineBreak +
  33. ' with the unicode related sources. It expects the following ' + sLineBreak +
  34. ' unicode.org''s files to be present in the same folder : ' + sLineBreak +
  35. ' * HangulSyllableType.txt ' + sLineBreak +
  36. ' * PropList.txt ' + sLineBreak +
  37. ' * UnicodeData.txt ' + sLineBreak +
  38. ' * allkeys.txt : Note that this file is the one provided for the CLDR root.' + sLineBreak +
  39. '' + sLineBreak +
  40. 'Usage : unihelper [<dataDir> <outputDir>] ' + sLineBreak +
  41. ' where ' + sLineBreak +
  42. ' dataDir : the directory where are stored the unicode files. The default' + sLineBreak +
  43. ' value is the program''s directory.' + sLineBreak +
  44. ' outputDir : The directory where the generated files will be stored. The' + sLineBreak +
  45. ' default value is the program''s directory.'+sLineBreak;
  46. function DumpCodePoint(ACodePoint : TCodePointRec) : string;
  47. begin
  48. Result := '';
  49. if (ACodePoint.LineType = 0) then
  50. WriteStr(Result,IntToHex(ACodePoint.CodePoint,4))
  51. else
  52. WriteStr(Result,IntToHex(ACodePoint.StartCodePoint,4),'..',IntToHex(ACodePoint.EndCodePoint,4));
  53. end;
  54. var
  55. dataPath, outputPath : string;
  56. stream, binStreamNE, binStreamOE, tmpStream : TMemoryStream;
  57. binaryStreamNE, binaryStreamOE : TMemoryStream;
  58. hangulSyllables : TCodePointRecArray;
  59. ucaBook : TUCA_DataBook;
  60. ucaPropBook : PUCA_PropBook;
  61. propList : TPropListLineRecArray;
  62. whiteSpaceCodePoints : TCodePointRecArray;
  63. props : TPropRecArray;
  64. numericTable : TNumericValueArray;
  65. decomposition : TDecompositionArray;
  66. decompositionBook : TDecompositionBook;
  67. data : TDataLineRecArray;
  68. //----------------
  69. lvl3table1 : T3lvlBmp1Table;
  70. lvl3table2 : T3lvlBmp2Table;
  71. lvl3table3 : T3lvlBmp3Table;
  72. //----------------
  73. s : ansistring;
  74. i, k, h : Integer;
  75. p : PDataLineRec;
  76. r : TDataLineRecArray;
  77. olvl3table1 : T3lvlOBmp1Table;
  78. olvl3table2 : T3lvlOBmp2Table;
  79. olvl3table3 : T3lvlOBmp3Table;
  80. //----------------
  81. hs, ls : Word;
  82. ucaFirstTable : TucaBmpFirstTable;
  83. ucaSecondTable : TucaBmpSecondTable;
  84. ucaoFirstTable : TucaoBmpFirstTable;
  85. ucaoSecondTable : TucaOBmpSecondTable;
  86. WL : Integer;
  87. serializedHeader : TSerializedCollationHeader;
  88. begin
  89. WriteLn(SUsage+sLineBreak);
  90. if (ParamCount > 0) then
  91. dataPath := IncludeTrailingPathDelimiter(ParamStr(1))
  92. else
  93. dataPath := ExtractFilePath(ParamStr(0));
  94. if (ParamCount > 1) then
  95. outputPath := IncludeTrailingPathDelimiter(ParamStr(2))
  96. else
  97. outputPath := dataPath;
  98. if not DirectoryExists(outputPath) then begin
  99. WriteLn('Directory not found : ',outputPath);
  100. if ForceDirectories(outputPath) then begin
  101. WriteLn(' directory created successfully');
  102. end else begin
  103. WriteLn(' fail to create directory.');
  104. Halt(1);
  105. end;
  106. end;
  107. if not(
  108. FileExists(dataPath + 'HangulSyllableType.txt') and
  109. FileExists(dataPath + 'PropList.txt') and
  110. FileExists(dataPath + 'UnicodeData.txt') and
  111. FileExists(dataPath + 'allkeys.txt')
  112. )
  113. then begin
  114. WriteLn('File(s) not found : HangulSyllableType.txt or PropList.txt or UnicodeData.txt or allkeys.txt .');
  115. Halt(1);
  116. end;
  117. binaryStreamNE := nil;
  118. binaryStreamOE := nil;
  119. binStreamOE := nil;
  120. binStreamNE := nil;
  121. tmpStream := nil;
  122. stream := TMemoryStream.Create();
  123. try
  124. binStreamNE := TMemoryStream.Create();
  125. binStreamOE := TMemoryStream.Create();
  126. tmpStream := TMemoryStream.Create();
  127. WriteLn('Load file HangulSyllableType.txt ...', DateTimeToStr(Now));
  128. stream.LoadFromFile(dataPath + 'HangulSyllableType.txt');
  129. stream.Position := 0;
  130. hangulSyllables := nil;
  131. ParseHangulSyllableTypes(stream,hangulSyllables);
  132. stream.Clear();
  133. WriteLn('Load file PropList.txt ...', DateTimeToStr(Now));
  134. stream.LoadFromFile(dataPath + 'PropList.txt');
  135. stream.Position := 0;
  136. propList := nil;
  137. ParseProps(stream,propList);
  138. stream.Clear();
  139. whiteSpaceCodePoints := FindCodePointsByProperty('White_Space',propList);
  140. writeln(' PropList Length = ',Length(propList));
  141. writeln(' White_Space Length = ',Length(whiteSpaceCodePoints));
  142. for i := Low(whiteSpaceCodePoints) to High(whiteSpaceCodePoints) do
  143. WriteLn(' ',DumpCodePoint(whiteSpaceCodePoints[i]):12,' , IsWhiteSpace = ',IsWhiteSpace(whiteSpaceCodePoints[i].CodePoint,whiteSpaceCodePoints));
  144. WriteLn('Load file UnicodeData.txt ...', DateTimeToStr(Now));
  145. stream.LoadFromFile(dataPath + 'UnicodeData.txt');
  146. stream.Position := 0;
  147. WriteLn('Parse file ...', DateTimeToStr(Now));
  148. data := nil;
  149. props := nil;
  150. Parse_UnicodeData(stream,props,numericTable,data,decomposition,hangulSyllables,whiteSpaceCodePoints);
  151. WriteLn('Decomposition building ...');
  152. MakeDecomposition(decomposition,decompositionBook);
  153. WriteLn('Load file UCA allkeys.txt ...', DateTimeToStr(Now));
  154. stream.LoadFromFile(dataPath + 'allkeys.txt');
  155. stream.Position := 0;
  156. ParseUCAFile(stream,ucaBook);
  157. { $IFDEF UCA_TEST}
  158. k := 0; WL := 0; ;
  159. for i := 0 to Length(ucaBook.Lines) - 1 do begin
  160. h := GetPropID(ucaBook.Lines[i].CodePoints[0],data);
  161. if (h <> -1) and
  162. ({props[h].HangulSyllable or} (props[h].DecompositionID <> -1))
  163. then begin
  164. Inc(k);
  165. ucaBook.Lines[i].Stored := False;
  166. end else begin
  167. ucaBook.Lines[i].Stored := True;
  168. if Length(ucaBook.Lines[i].Weights) > WL then
  169. WL := Length(ucaBook.Lines[i].Weights);
  170. end;
  171. end;
  172. WriteLn(
  173. 'UCA, Version = ',ucaBook.Version,
  174. '; entries count = ',Length(ucaBook.Lines),
  175. '; characters (Decomposition) count = ',k,
  176. '; Max Weights Length = ',WL
  177. );
  178. { $ENDIF UCA_TEST}
  179. WriteLn('Construct UCA Property Book ...');
  180. ucaPropBook := nil;
  181. MakeUCA_Props(@ucaBook,ucaPropBook);
  182. {$IFDEF UCA_TEST}
  183. uca_CheckProp_1(ucaBook,ucaPropBook);
  184. uca_CheckProp_x(ucaBook,ucaPropBook);
  185. {$ENDIF UCA_TEST}
  186. WriteLn('Construct UCA BMP tables ...');
  187. MakeUCA_BmpTables(ucaFirstTable,ucaSecondTable,ucaPropBook);
  188. WriteLn(' UCA BMP Second Table Length = ',Length(ucaSecondTable));
  189. {$IFDEF UCA_TEST}
  190. uca_CheckProp_1y(ucaBook,ucaPropBook,@ucaFirstTable,@ucaSecondTable);
  191. {$ENDIF UCA_TEST}
  192. WriteLn('Construct UCA OBMP tables ...');
  193. MakeUCA_OBmpTables(ucaoFirstTable,ucaoSecondTable,ucaPropBook);
  194. WriteLn(' UCA OBMP Second Table Length = ',Length(ucaoSecondTable));
  195. {$IFDEF UCA_TEST}
  196. uca_CheckProp_2y(ucaBook,ucaPropBook,@ucaoFirstTable,@ucaoSecondTable);
  197. {$ENDIF UCA_TEST}
  198. binaryStreamNE := TMemoryStream.Create();
  199. binaryStreamOE := TMemoryStream.Create();
  200. WriteLn('Generate UCA Props tables ...');
  201. binStreamNE.Clear();
  202. binStreamOE.Clear();
  203. GenerateLicenceText(binStreamNE);
  204. GenerateLicenceText(binStreamOE);
  205. GenerateUCA_PropTable(binStreamNE,ucaPropBook,ENDIAN_NATIVE);
  206. GenerateUCA_PropTable(binStreamOE,ucaPropBook,ENDIAN_NON_NATIVE);
  207. WriteLn('Generate UCA BMP tables ...');
  208. stream.Clear();
  209. GenerateLicenceText(stream);
  210. GenerateUCA_Head(stream,@ucaBook,ucaPropBook);
  211. GenerateUCA_BmpTables(stream,binStreamNE,binStreamOE,ucaFirstTable,ucaSecondTable);
  212. WriteLn('Generate UCA OBMP tables ...');
  213. GenerateUCA_OBmpTables(stream,binStreamNE,binStreamOE,ucaoFirstTable,ucaoSecondTable);
  214. stream.SaveToFile(outputPath + 'ucadata.inc');
  215. s := outputPath + 'ucadata.inc';
  216. binStreamNE.SaveToFile(GenerateEndianIncludeFileName(s,ENDIAN_NATIVE));
  217. binStreamOE.SaveToFile(GenerateEndianIncludeFileName(s,ENDIAN_NON_NATIVE));
  218. binStreamNE.Clear();
  219. binStreamOE.Clear();
  220. // Binary DUCET
  221. FillChar(serializedHeader,SizeOf(TSerializedCollationHeader),0);
  222. StringToByteArray(ucaBook.Version,serializedHeader.Version);
  223. StringToByteArray('DUCET',serializedHeader.CollationName); //'Default Unicode Collation Element Table (DUCET)';
  224. serializedHeader.VariableWeight := Ord(ucaBook.VariableWeight);
  225. SetBit(serializedHeader.Backwards,0,ucaBook.Backwards[0]);
  226. SetBit(serializedHeader.Backwards,1,ucaBook.Backwards[1]);
  227. SetBit(serializedHeader.Backwards,2,ucaBook.Backwards[2]);
  228. SetBit(serializedHeader.Backwards,3,ucaBook.Backwards[3]);
  229. serializedHeader.BMP_Table1Length := Length(ucaFirstTable);
  230. serializedHeader.BMP_Table2Length := Length(TucaBmpSecondTableItem) *
  231. (Length(ucaSecondTable) * SizeOf(UInt24));
  232. serializedHeader.OBMP_Table1Length := Length(ucaoFirstTable) * SizeOf(Word);
  233. serializedHeader.OBMP_Table2Length := Length(TucaOBmpSecondTableItem) *
  234. (Length(ucaoSecondTable) * SizeOf(UInt24));
  235. serializedHeader.PropCount := ucaPropBook^.ItemSize;
  236. serializedHeader.VariableLowLimit := ucaPropBook^.VariableLowLimit;
  237. serializedHeader.VariableHighLimit := ucaPropBook^.VariableHighLimit;
  238. binaryStreamNE.Write(serializedHeader,SizeOf(serializedHeader));
  239. ReverseRecordBytes(serializedHeader);
  240. binaryStreamOE.Write(serializedHeader,SizeOf(serializedHeader));
  241. GenerateBinaryUCA_BmpTables(binaryStreamNE,binaryStreamOE,ucaFirstTable,ucaSecondTable);
  242. GenerateBinaryUCA_OBmpTables(binaryStreamNE,binaryStreamOE,ucaoFirstTable,ucaoSecondTable);
  243. GenerateBinaryUCA_PropTable(binaryStreamNE,binaryStreamOE,ucaPropBook);
  244. binaryStreamNE.SaveToFile(
  245. outputPath + Format('collation_ducet_%s.bco',[ENDIAN_SUFFIX[ENDIAN_NATIVE]])
  246. );
  247. binaryStreamOE.SaveToFile(
  248. outputPath + Format('collation_ducet_%s.bco',[ENDIAN_SUFFIX[ENDIAN_NON_NATIVE]])
  249. );
  250. // Binary DUCET - END
  251. stream.Clear();
  252. GenerateLicenceText(stream);
  253. WriteLn('File parsed ...', DateTimeToStr(Now));
  254. WriteLn(' Props Len = ',Length(props));
  255. WriteLn(' Data Len = ',Length(data));
  256. {WriteLn('BMP Tables building ...', DateTimeToStr(Now));
  257. MakeBmpTables(firstTable,secondTable,props,data);
  258. WriteLn(' First Table length = ',Length(firstTable));
  259. WriteLn(' Second Table length = ',Length(secondTable));}
  260. WriteLn('BMP Tables building ...', DateTimeToStr(Now));
  261. MakeBmpTables3Levels(lvl3table1,lvl3table2,lvl3table3,data);
  262. WriteLn(' 3 Levels Tables :');
  263. WriteLn(' Len 1 = ',Length(lvl3table1));
  264. WriteLn(' Len 2 = ',Length(lvl3table2));
  265. WriteLn(' Len 3 = ',Length(lvl3table3));
  266. for i := 0 to 255 do begin
  267. for k := 0 to 15 do begin
  268. for h := 0 to 15 do begin
  269. if lvl3table3[lvl3table2[lvl3table1[i]][k]][h] <>
  270. GetPropID(256*i + 16*k +h,data)
  271. then begin
  272. writeln('3 levels errors, i=',i,'; k=',k,'; h=',h);
  273. end;
  274. end;
  275. end;
  276. end;
  277. binStreamNE.Clear();
  278. binStreamOE.Clear();
  279. WriteLn('Source generation ...', DateTimeToStr(Now));
  280. WriteLn('BMP Tables sources ...', DateTimeToStr(Now));
  281. Generate3lvlBmpTables(stream,lvl3table1,lvl3table2,lvl3table3);
  282. WriteLn('Properties Table sources ...', DateTimeToStr(Now));
  283. tmpStream.Clear();
  284. GenerateNumericTable(tmpStream,numericTable,True);
  285. tmpStream.SaveToFile(outputPath + 'unicodenumtable.pas');
  286. tmpStream.Clear();
  287. GeneratePropTable(binStreamNE,props,ENDIAN_NATIVE);
  288. GeneratePropTable(binStreamOE,props,ENDIAN_NON_NATIVE);
  289. //-------------------------------------------
  290. r := Compress(data);
  291. //-------------------
  292. WriteLn('OBMP Tables building ...', DateTimeToStr(Now));
  293. MakeOBmpTables3Levels(olvl3table1,olvl3table2,olvl3table3,r);
  294. WriteLn(' 3 Levels Tables :');
  295. WriteLn(' Len 1 = ',Length(olvl3table1));
  296. WriteLn(' Len 2 = ',Length(olvl3table2));
  297. WriteLn(' Len 3 = ',Length(olvl3table3));
  298. for i := 0 to 1023 do begin
  299. for k := 0 to 31 do begin
  300. for h := 0 to 31 do begin
  301. if olvl3table3[olvl3table2[olvl3table1[i]][k]][h] <>
  302. GetPropID(ToUCS4(HIGH_SURROGATE_BEGIN + i,LOW_SURROGATE_BEGIN + (k*32) + h),data)
  303. then begin
  304. writeln('3, OBMP levels errors, i=',i,'; k=',k,'; h=',h);
  305. end;
  306. end;
  307. end;
  308. end;
  309. WriteLn('OBMP Tables sources ...', DateTimeToStr(Now));
  310. Generate3lvlOBmpTables(stream,olvl3table1,olvl3table2,olvl3table3);
  311. //---------------------
  312. WriteLn('Decomposition Table sources ...', DateTimeToStr(Now));
  313. GenerateDecompositionBookTable(binStreamNE,decompositionBook,ENDIAN_NATIVE);
  314. GenerateDecompositionBookTable(binStreamOE,decompositionBook,ENDIAN_NON_NATIVE);
  315. stream.SaveToFile(outputPath + 'unicodedata.inc');
  316. binStreamNE.SaveToFile(outputPath + 'unicodedata_'+ENDIAN_SUFFIX[ENDIAN_NATIVE]+'.inc');
  317. binStreamOE.SaveToFile(outputPath + 'unicodedata_'+ENDIAN_SUFFIX[ENDIAN_NON_NATIVE]+'.inc');
  318. binStreamNE.Clear();
  319. binStreamOE.Clear();
  320. h := -1;
  321. for i := Low(data) to High(data) do
  322. if (data[i].CodePoint > $FFFF) then begin
  323. h := i;
  324. Break;
  325. end;
  326. stream.Clear();
  327. for i := h to High(data) do begin
  328. p := @data[i];
  329. if (p^.LineType = 0) then begin
  330. FromUCS4(p^.CodePoint,hs,ls);
  331. //k := GetProp(hs,ls,props,ofirstTable,osecondTable)^.PropID;
  332. k := GetProp(
  333. (hs-HIGH_SURROGATE_BEGIN),(ls-LOW_SURROGATE_BEGIN),
  334. props,olvl3table1,olvl3table2,olvl3table3
  335. )^.PropID;
  336. if (p^.PropID <> k) then begin
  337. s := Format('#%d-%d #%d',[p^.CodePoint,p^.PropID,k]) + sLineBreak;
  338. stream.Write(s[1],Length(s));
  339. end;
  340. end else begin
  341. for h := p^.StartCodePoint to p^.EndCodePoint do begin
  342. FromUCS4(h,hs,ls);
  343. //k := GetProp(hs,ls,props,ofirstTable,osecondTable)^.PropID;
  344. k := GetProp(
  345. (hs-HIGH_SURROGATE_BEGIN),(ls-LOW_SURROGATE_BEGIN),
  346. props,olvl3table1,olvl3table2,olvl3table3
  347. )^.PropID;
  348. if (p^.PropID <> k) then begin
  349. s := Format('##%d;%d-%d #%d',[p^.StartCodePoint,p^.EndCodePoint,p^.PropID,k]) + sLineBreak;
  350. stream.Write(s[1],Length(s));
  351. Break
  352. end;
  353. end;
  354. end;
  355. end;
  356. stream.SaveToFile(outputPath + 'diff-obmp.txt');
  357. stream.Clear();
  358. for i := Low(data) to High(data) do begin
  359. p := @data[i];
  360. if (p^.LineType = 0) then begin
  361. k := GetPropID(p^.CodePoint,r);
  362. if (p^.PropID <> k) then begin
  363. s := Format('#%d-%d #%d',[p^.CodePoint,p^.PropID,k]) + sLineBreak;
  364. stream.Write(s[1],Length(s));
  365. end;
  366. end else begin
  367. for h := p^.StartCodePoint to p^.EndCodePoint do begin
  368. k := GetPropID(h,r);
  369. if (p^.PropID <> k) then begin
  370. s := Format('##%d;%d-%d #%d',[p^.StartCodePoint,p^.EndCodePoint,p^.PropID,k]) + sLineBreak;
  371. stream.Write(s[1],Length(s));
  372. Break
  373. end;
  374. end;
  375. end;
  376. end;
  377. stream.SaveToFile(outputPath + 'diff.txt');
  378. stream.Clear();
  379. for i := Low(r) to High(r) do begin
  380. p := @r[i];
  381. if (p^.LineType = 0) then begin
  382. k := GetPropID(p^.CodePoint,data);
  383. if (p^.PropID <> k) then begin
  384. s := Format('#%d-%d #%d',[p^.CodePoint,p^.PropID,k]) + sLineBreak;
  385. stream.Write(s[1],Length(s));
  386. end;
  387. end else begin
  388. for h := p^.StartCodePoint to p^.EndCodePoint do begin
  389. k := GetPropID(h,r);
  390. if (p^.PropID <> k) then begin
  391. s := Format('##%d;%d-%d #%d',[p^.StartCodePoint,p^.EndCodePoint,p^.PropID,k]) + sLineBreak;
  392. stream.Write(s[1],Length(s));
  393. Break
  394. end;
  395. end;
  396. end;
  397. end;
  398. stream.SaveToFile(outputPath + 'diff2.txt');
  399. finally
  400. binaryStreamOE.Free();
  401. binaryStreamNE.Free();
  402. tmpStream.Free();
  403. binStreamOE.Free();
  404. binStreamNE.Free();
  405. stream.Free();
  406. end;
  407. end.