Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1129 lines
28 KiB

  1. //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  2. //
  3. // Copyright (c) 2001 Microsoft Corporation. All rights reserved.
  4. //
  5. // Module:
  6. // volcano/dll/BoxApi.c
  7. //
  8. // Description:
  9. // Implement external Box input API for DLL.
  10. //
  11. // Author:
  12. // hrowley
  13. //
  14. //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
  15. #include <stdlib.h>
  16. #include "volcanop.h"
  17. #include "zillap.h"
  18. #include "fugu.h"
  19. #if defined(USE_HOUND) || defined(USE_ZILLAHOUND)
  20. # include "math16.h"
  21. # include "hound.h"
  22. #endif
  23. #include "tpgHandle.h"
  24. // Sanity check limits on guide structures.
  25. #define GUIDE_MIN_COORD ((INT)0xC0000000)
  26. #define GUIDE_MAX_COORD ((INT)0x3FFFFFFF)
  27. #define GUIDE_MIN_BOXES 1
  28. #define GUIDE_MAX_BOXES (32 * 1024)
  29. #define GUIDE_MIN_SIZE 8
  30. #define GUIDE_MAX_SIZE (256 * 1024)
  31. // iUseCount tells if we have successfully loaded and is incremented to 1 when that happens.
  32. static int g_iUseCount = 0;
  33. // COM code for IFELang3
  34. #ifdef USE_IFELANG3
  35. # include <windows.h>
  36. # include <ole2.h>
  37. # include <objbase.h>
  38. // Make sure the GUIDs defined in imlang.h actually get instantiated
  39. # define INITGUID
  40. # include "imlang.h"
  41. #endif
  42. // g_hInstanceDll is refered to to load resources.
  43. HINSTANCE g_hInstanceDll;
  44. // g_hInstanceDllCode is refered to to find the code DLL..
  45. HINSTANCE g_hInstanceDllCode;
  46. // Global data loaded by LoadCharRec.
  47. LOCRUN_INFO g_locRunInfo;
  48. // What language the recognizer is recognizing
  49. wchar_t *g_szRecognizerLanguage=NULL;
  50. //#define DEBUG_LOG_API
  51. #ifdef DEBUG_LOG_API
  52. #include <stdio.h>
  53. static void LogMessage(char *format, ...)
  54. {
  55. FILE *f=fopen("c:/log.txt","a");
  56. va_list marker;
  57. va_start(marker,format);
  58. vfprintf(f,format,marker);
  59. va_end(marker);
  60. fclose(f);
  61. }
  62. #else
  63. static void LogMessage(char *format, ...)
  64. {
  65. va_list marker;
  66. va_start(marker,format);
  67. va_end(marker);
  68. }
  69. #endif
  70. // Forward declaration for function to unload the recognizer
  71. // Unload the recognizer. The flag indicates whether we should try to unload IFELang3
  72. // as well. This flag should usually be TRUE, but must be FALSE during a call from DllMain(),
  73. // because it is not safe to do COM operations inside DllMain().
  74. BOOL HwxUnconfig(BOOL fCanUnloadIFELang3);
  75. static CRITICAL_SECTION g_csRecognizer = {0};
  76. static BOOL g_bInitializedRecognizerCS = FALSE;
  77. // Capture the dll handle, also handle unloading.
  78. BOOL WINAPI DllMain(HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
  79. {
  80. if (dwReason == DLL_PROCESS_ATTACH)
  81. {
  82. // If we are using resources and they are in another DLL, we need to load that DLL.
  83. # if defined(USE_RESOURCES)
  84. wchar_t aFileName[_MAX_PATH];
  85. int length;
  86. // Build up name of data DLL from the name of this DLL. We replace .DLL with R.DLL.
  87. length = GetModuleFileName(hDll, aFileName, _MAX_PATH);
  88. if (length < 5 || _MAX_PATH - 1 < length)
  89. {
  90. ASSERT(length >= 5);
  91. return FALSE;
  92. }
  93. wcscpy(aFileName + length - 4, L"R.DLL");
  94. // Load the data library.
  95. g_hInstanceDll = LoadLibraryEx(aFileName, NULL, LOAD_LIBRARY_AS_DATAFILE);
  96. ASSERT(g_hInstanceDll);
  97. // If we can't find our resources, just give up. Nothing will work.
  98. if (!g_hInstanceDll)
  99. {
  100. return FALSE;
  101. }
  102. if (!InitializeCriticalSectionAndSpinCount(&g_csRecognizer, 4000))
  103. {
  104. return FALSE;
  105. }
  106. else
  107. {
  108. g_bInitializedRecognizerCS = TRUE;
  109. }
  110. # else
  111. g_hInstanceDll = hDll;
  112. # endif
  113. // init the handle manager
  114. if (FALSE == initTpgHandleManager())
  115. {
  116. return FALSE;
  117. }
  118. // Always let the Wisp code know where the code DLL is.
  119. g_hInstanceDllCode = hDll;
  120. }
  121. else if (dwReason == DLL_PROCESS_DETACH)
  122. {
  123. BOOL fRet;
  124. // If we haven't cleaned up.
  125. // In the future, we should force g_iUseCount to be zero, to make
  126. // sure the client has cleaned us up properly. Until everyone is
  127. // using the WISP API we cannot enforce this, because the old API
  128. // has no "clean up" call.
  129. // ASSERT(g_iUseCount == 0);
  130. if (g_iUseCount > 0)
  131. {
  132. // Set the use count to 1 to force a cleanup
  133. g_iUseCount = 1;
  134. // Clean up, but don't do COM calls to discard IFELang3,
  135. // because we can't make COM calls inside DllMain.
  136. fRet = HwxUnconfig(FALSE);
  137. }
  138. else
  139. {
  140. // If we have already cleaned up, return
  141. fRet = TRUE;
  142. }
  143. # if defined(USE_RESOURCES)
  144. if (!FreeLibrary(g_hInstanceDll))
  145. {
  146. fRet = FALSE;
  147. }
  148. if (g_bInitializedRecognizerCS)
  149. {
  150. DeleteCriticalSection(&g_csRecognizer);
  151. g_bInitializedRecognizerCS = FALSE;
  152. }
  153. # endif
  154. // close the handle manager
  155. closeTpgHandleManager();
  156. return fRet;
  157. }
  158. return TRUE;
  159. }
  160. // HwxConfig
  161. //
  162. // Initialize the recognizer when it loads.
  163. #ifdef USE_RESOURCES
  164. #include "res.h"
  165. BOOL HwxConfig()
  166. {
  167. const ZILLADB_HEADER *pHeader;
  168. EnterCriticalSection(&g_csRecognizer);
  169. LogMessage("HwxConfig()\n");
  170. if (g_iUseCount > 0) {
  171. goto exitSuccess;
  172. }
  173. // Load the Locale database.
  174. if (!LocRunLoadRes(
  175. &g_locRunInfo, g_hInstanceDll, RESID_LOCRUN, VOLCANO_RES
  176. )) {
  177. ASSERT(("Error in LocRunLoadRes", FALSE));
  178. goto exitFailure;
  179. }
  180. // Load the recognizer databases.
  181. if (!LoadCharRec(g_hInstanceDll)) {
  182. ASSERT(("Error in LoadCharRec", FALSE));
  183. goto exitFailure;
  184. }
  185. pHeader = (ZILLADB_HEADER*)DoLoadResource(NULL, g_hInstanceDll, RESID_ZILLA, VOLCANO_RES);
  186. if (pHeader == NULL) {
  187. ASSERT(("Couldn't load zilla database", FALSE));
  188. goto exitFailure;
  189. }
  190. g_szRecognizerLanguage = ExternAlloc(sizeof(wchar_t) * (wcslen(pHeader->locale) + 1));
  191. if (g_szRecognizerLanguage == NULL) {
  192. ASSERT(("Couldn't generate recognizer language string", FALSE));
  193. goto exitFailure;
  194. }
  195. wcscpy(g_szRecognizerLanguage, pHeader->locale);
  196. // Configure the factoid table
  197. if (!FactoidTableConfig(&g_locRunInfo, g_szRecognizerLanguage))
  198. {
  199. goto exitFailure;
  200. }
  201. // Configure the lattice code.
  202. if (!LatticeConfig(g_hInstanceDll)) {
  203. ASSERT(("Error in LatticeConfig", FALSE));
  204. goto exitFailure;
  205. }
  206. exitSuccess:
  207. g_iUseCount++;
  208. LeaveCriticalSection(&g_csRecognizer);
  209. return TRUE;
  210. exitFailure:
  211. LeaveCriticalSection(&g_csRecognizer);
  212. return FALSE;
  213. }
  214. // Unload the recognizer. The flag indicates whether we should try to unload IFELang3
  215. // as well. This flag should usually be TRUE, but must be FALSE during a call from DllMain(),
  216. // because it is not safe to do COM operations inside DllMain().
  217. BOOL HwxUnconfig(BOOL fCanUnloadIFELang3)
  218. {
  219. BOOL ok = TRUE;
  220. EnterCriticalSection(&g_csRecognizer);
  221. if (g_iUseCount == 0) {
  222. ASSERT(("HwxUnconfig() called more times that HwxConfig()\n", 0));
  223. LeaveCriticalSection(&g_csRecognizer);
  224. return FALSE;
  225. }
  226. g_iUseCount--;
  227. if (g_iUseCount == 0)
  228. {
  229. if (g_szRecognizerLanguage != NULL)
  230. {
  231. ExternFree(g_szRecognizerLanguage);
  232. }
  233. if (!UnloadCharRec()) ok = FALSE;
  234. if (!LatticeUnconfig()) ok = FALSE;
  235. #ifdef USE_IFELANG3
  236. if (fCanUnloadIFELang3 && !LatticeUnconfigIFELang3()) ok = FALSE;
  237. #endif
  238. FactoidTableUnconfig();
  239. }
  240. LeaveCriticalSection(&g_csRecognizer);
  241. return ok;
  242. }
  243. # else
  244. BOOL HwxConfigEx(wchar_t *pLocale, wchar_t *pLocaleDir, wchar_t *pRecogDir)
  245. {
  246. LogMessage("HwxConfigEx()\n");
  247. if (g_iUseCount > 0) {
  248. g_iUseCount++;
  249. return(TRUE);
  250. }
  251. g_szRecognizerLanguage = ExternAlloc(sizeof(wchar_t) * (wcslen(pLocale) + 1));
  252. if (g_szRecognizerLanguage==NULL) {
  253. ASSERT(("Out of memory copying recognizer language", FALSE));
  254. return FALSE;
  255. }
  256. wcscpy(g_szRecognizerLanguage, pLocale);
  257. // Load the Locale database.
  258. if (!LocRunLoadFile(&g_locRunInfo, pRecogDir)) {
  259. ASSERT(("Error in LocRunLoadFile", FALSE));
  260. return FALSE;
  261. }
  262. // Configure the factoid table
  263. if (!FactoidTableConfig(&g_locRunInfo, g_szRecognizerLanguage))
  264. {
  265. return FALSE;
  266. }
  267. // Load the recognizer databases.
  268. // They are loaded in this order: otter, zilla, crane/prob or hawk,
  269. if (!LoadCharRec(pRecogDir)) {
  270. ASSERT(("Error in LoadCharRec", FALSE));
  271. return FALSE;
  272. }
  273. // Configure the lattice code.
  274. // Loads the following databases: unigrams, bigrams, class bigrams, tuning, centipede, free input
  275. if (!LatticeConfigFile(pRecogDir)) {
  276. ASSERT(("Error in LatticeConfig", FALSE));
  277. return FALSE;
  278. }
  279. g_iUseCount++;
  280. return TRUE;
  281. }
  282. // Initialize the recognizer partially.
  283. // When iLevel is set to 0, only the locale database, tuning database,
  284. // otter and zilla (and other core recognizers) are loaded.
  285. // When iLevel is set to 1, all the above databases are loaded, as well as
  286. // unigrams and crane/hawk.
  287. BOOL HwxConfigExPartial(wchar_t *pLocale, wchar_t *pRecogDir, int iLevel)
  288. {
  289. extern OTTER_LOAD_INFO g_OtterLoadInfo;
  290. extern FUGU_LOAD_INFO g_FuguLoadInfo;
  291. extern JAWS_LOAD_INFO g_JawsLoadInfo;
  292. extern SOLE_LOAD_INFO g_SoleLoadInfo;
  293. #if defined(USE_HOUND) || defined(USE_ZILLAHOUND)
  294. extern LOAD_INFO g_HoundLoadInfo;
  295. #endif
  296. #ifdef USE_OLD_DATABASES
  297. extern CRANE_LOAD_INFO g_CraneLoadInfo;
  298. #else
  299. extern LOAD_INFO g_HawkLoadInfo;
  300. #endif
  301. LogMessage("HwxConfigExPartial()\n");
  302. if (g_iUseCount > 0) {
  303. g_iUseCount++;
  304. return(TRUE);
  305. }
  306. g_szRecognizerLanguage = ExternAlloc(sizeof(wchar_t) * (wcslen(pLocale) + 1));
  307. if (g_szRecognizerLanguage==NULL) {
  308. ASSERT(("Out of memory copying recognizer language", FALSE));
  309. return FALSE;
  310. }
  311. wcscpy(g_szRecognizerLanguage, pLocale);
  312. LatticeConfigInit();
  313. if (iLevel >= 0) {
  314. // Load the Locale database.
  315. if (!LocRunLoadFile(&g_locRunInfo, pRecogDir)) {
  316. ASSERT(("Error in LocRunLoadFile", FALSE));
  317. return FALSE;
  318. }
  319. // Configure the factoid table
  320. if (!FactoidTableConfig(&g_locRunInfo, g_szRecognizerLanguage))
  321. {
  322. return FALSE;
  323. }
  324. // Load the tuning info
  325. if (!VTuneLoadFile(&g_vtuneInfo, pRecogDir)) {
  326. ASSERT(("Error in VTuneLoadFile", FALSE));
  327. return FALSE;
  328. }
  329. // Load the tuning info
  330. if (!TTuneLoadFile(&g_ttuneInfo, pRecogDir)) {
  331. ASSERT(("Error in TTuneLoadFile", FALSE));
  332. return FALSE;
  333. }
  334. if (JawsLoadFile(&g_JawsLoadInfo, pRecogDir))
  335. {
  336. // Load the Fugu database
  337. if (!FuguLoadFile(&g_FuguLoadInfo, pRecogDir, &g_locRunInfo))
  338. {
  339. ASSERT(("Error in FuguLoadFile", FALSE));
  340. return FALSE;
  341. }
  342. // Load the Sole database
  343. if (!SoleLoadFile(&g_SoleLoadInfo, pRecogDir, &g_locRunInfo))
  344. {
  345. ASSERT(("Error in SoleLoadFile", FALSE));
  346. return FALSE;
  347. }
  348. g_fUseJaws = TRUE;
  349. }
  350. else
  351. {
  352. // Load the Otter database
  353. if (!OtterLoadFile(&g_locRunInfo, &g_OtterLoadInfo, pRecogDir))
  354. {
  355. ASSERT(("Error in OtterLoadFile", FALSE));
  356. return FALSE;
  357. }
  358. g_fUseJaws = FALSE;
  359. }
  360. #if defined(USE_ZILLA) || defined(USE_ZILLAHOUND)
  361. // Load the Zilla database
  362. if (!ZillaLoadFile(&g_locRunInfo, pRecogDir, TRUE)) {
  363. ASSERT(("Error in ZillaLoadFile", FALSE));
  364. return FALSE;
  365. }
  366. #endif
  367. #if defined(USE_HOUND)
  368. // Load the Hound database
  369. if (!HoundLoadFile(&g_locRunInfo, &g_HoundLoadInfo, pRecogDir)) {
  370. ASSERT(("Error in HoundLoadFile", FALSE));
  371. return FALSE;
  372. }
  373. #endif
  374. g_fUseZillaHound = FALSE;
  375. #if defined(USE_ZILLAHOUND)
  376. // Load the Hound & Hound-Zilla databases (This is optional).
  377. if (HoundLoadFile(&g_locRunInfo, &g_HoundLoadInfo, pRecogDir)) {
  378. if (ZillaHoundLoadFile(pRecogDir)) {
  379. g_fUseZillaHound = TRUE;
  380. }
  381. else
  382. {
  383. HoundUnLoadFile(&g_HoundLoadInfo);
  384. }
  385. }
  386. #endif
  387. }
  388. if (iLevel >= 1) {
  389. // Load unigrams
  390. if (!UnigramLoadFile(&g_locRunInfo, &g_unigramInfo, pRecogDir)) {
  391. ASSERT(("Error in UnigramLoadFile", FALSE));
  392. return FALSE;
  393. }
  394. #ifndef USE_OLD_DATABASES
  395. // Load the Hawk database.
  396. if (!HawkLoadFile(&g_locRunInfo, &g_HawkLoadInfo, pRecogDir)) {
  397. ASSERT(("Error in HawkLoadFile", FALSE));
  398. return FALSE;
  399. }
  400. #else
  401. // Load Crane
  402. if (!CraneLoadFile(&g_locRunInfo,&g_CraneLoadInfo, pRecogDir)) {
  403. ASSERT(("Error in CraneLoadFile", FALSE));
  404. return FALSE;
  405. }
  406. #endif
  407. }
  408. g_iUseCount++;
  409. return TRUE;
  410. }
  411. // Unload the recognizer. The flag indicates whether we should try to unload IFELang3
  412. // as well. This flag should usually be TRUE, but must be FALSE during a call from DllMain(),
  413. // because it is not safe to do COM operations inside DllMain().
  414. BOOL HwxUnconfig(BOOL fCanUnloadIFELang3)
  415. {
  416. BOOL ok = TRUE;
  417. if (g_iUseCount == 0) {
  418. ASSERT(("HwxUnconfig() called more times that HwxConfig()\n", 0));
  419. return FALSE;
  420. }
  421. g_iUseCount--;
  422. if (g_iUseCount > 0) return TRUE;
  423. if (g_szRecognizerLanguage != NULL)
  424. {
  425. ExternFree(g_szRecognizerLanguage);
  426. }
  427. if (!LocRunUnloadFile(&g_locRunInfo)) ok = FALSE;
  428. if (!UnloadCharRec()) ok = FALSE;
  429. if (!LatticeUnconfigFile()) ok = FALSE;
  430. #ifdef USE_IFELANG3
  431. if (fCanUnloadIFELang3 && !LatticeUnconfigIFELang3()) ok = FALSE;
  432. #endif
  433. FactoidTableUnconfig();
  434. return ok;
  435. }
  436. # endif
  437. ////
  438. // HwxCreate
  439. //
  440. // Create an HRC.
  441. ////
  442. HRC HwxCreate(HRC hrcTemplate)
  443. {
  444. VRC *pVRC;
  445. LogMessage("HwxCreate(%08X)\n",hrcTemplate);
  446. // Alloc the VRC.
  447. pVRC = ExternAlloc(sizeof(VRC));
  448. if (!pVRC) {
  449. SetLastError(ERROR_OUTOFMEMORY);
  450. goto error1;
  451. }
  452. // Initialize it.
  453. pVRC->fBoxedInput = TRUE;
  454. pVRC->fHaveInput = FALSE;
  455. pVRC->fEndInput = FALSE;
  456. pVRC->fBeginProcess = FALSE;
  457. pVRC->pLatticePath = (LATTICE_PATH *)0;
  458. if (!hrcTemplate) {
  459. pVRC->pLattice = AllocateLattice();
  460. if (!pVRC->pLattice) {
  461. SetLastError(ERROR_OUTOFMEMORY);
  462. goto error2;
  463. }
  464. } else {
  465. VRC *pVRCTemplate = (VRC *)hrcTemplate;
  466. if (!pVRCTemplate->pLattice || !pVRCTemplate->fBoxedInput) {
  467. SetLastError(ERROR_INVALID_HANDLE);
  468. }
  469. pVRC->pLattice = CreateCompatibleLattice(
  470. pVRCTemplate->pLattice
  471. );
  472. if (!pVRC->pLattice) {
  473. SetLastError(ERROR_OUTOFMEMORY);
  474. goto error2;
  475. }
  476. }
  477. LogMessage("HwxCreate(%08X) -> %08X\n", hrcTemplate, pVRC);
  478. // Success, cast to HRC and return it.
  479. return (HRC)pVRC;
  480. error2:
  481. ExternFree(pVRC);
  482. error1:
  483. return (HRC)0;
  484. }
  485. ////
  486. // HwxDestroy
  487. //
  488. // Destroy an HRC.
  489. ////
  490. BOOL HwxDestroy(HRC hrc)
  491. {
  492. VRC *pVRC = (VRC *)hrc;
  493. LogMessage("HwxDestroy(%08X)\n",hrc);
  494. // Did we get a handle? Is if a free input handle?
  495. if (!hrc || !pVRC->fBoxedInput) {
  496. SetLastError(ERROR_INVALID_HANDLE);
  497. return FALSE;
  498. }
  499. // Free the lattice. Should it be an error if there is not one?
  500. if (pVRC->pLattice) {
  501. FreeLattice(pVRC->pLattice);
  502. } else {
  503. ASSERT(pVRC->pLattice);
  504. }
  505. // Free the lattice path.
  506. if (pVRC->pLatticePath) {
  507. FreeLatticePath(pVRC->pLatticePath);
  508. }
  509. // Free the VRC itself.
  510. ExternFree(pVRC);
  511. return TRUE;
  512. }
  513. ////
  514. // HwxSetGuide
  515. //
  516. // Sets the guide structure to use.
  517. ////
  518. BOOL HwxSetGuide(HRC hrc, HWXGUIDE *pGuide)
  519. {
  520. VRC *pVRC = (VRC *)hrc;
  521. LogMessage("HwxSetGuide(%08X,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)\n", hrc,
  522. pGuide->cHorzBox,
  523. pGuide->cVertBox,
  524. pGuide->xOrigin,
  525. pGuide->yOrigin,
  526. pGuide->cxBox,
  527. pGuide->cyBox,
  528. pGuide->cxOffset,
  529. pGuide->cyOffset,
  530. pGuide->cxWriting,
  531. pGuide->cyWriting,
  532. pGuide->cyMid,
  533. pGuide->cyBase,
  534. pGuide->nDir);
  535. // Did we get a handle? Does it have a lattice?
  536. if (!hrc || !pVRC->pLattice || !pVRC->fBoxedInput)
  537. {
  538. SetLastError(ERROR_INVALID_HANDLE);
  539. return FALSE;
  540. }
  541. // Make sure that we haven't begun receiving input
  542. if (pVRC->fHaveInput || pVRC->fEndInput)
  543. {
  544. SetLastError(ERROR_CAN_NOT_COMPLETE);
  545. return FALSE;
  546. }
  547. // Validate the individual GUIDE values are not totally out of bounds.
  548. if ((pGuide == (HWXGUIDE *)NULL) ||
  549. (pGuide->cHorzBox < GUIDE_MIN_BOXES) || (GUIDE_MAX_BOXES < pGuide->cHorzBox) ||
  550. (pGuide->cVertBox < GUIDE_MIN_BOXES) || (GUIDE_MAX_BOXES < pGuide->cVertBox) ||
  551. (pGuide->xOrigin < GUIDE_MIN_COORD) || (GUIDE_MAX_COORD < pGuide->xOrigin) ||
  552. (pGuide->yOrigin < GUIDE_MIN_COORD) || (GUIDE_MAX_COORD < pGuide->yOrigin) ||
  553. (GUIDE_MAX_SIZE < pGuide->cxOffset) ||
  554. (GUIDE_MAX_SIZE < pGuide->cyOffset) ||
  555. (pGuide->cxBox < GUIDE_MIN_SIZE) || (GUIDE_MAX_SIZE < pGuide->cxBox) ||
  556. (pGuide->cyBox < GUIDE_MIN_SIZE) || (GUIDE_MAX_SIZE < pGuide->cyBox) ||
  557. (pGuide->cxWriting < GUIDE_MIN_SIZE) || (GUIDE_MAX_SIZE < pGuide->cxWriting) ||
  558. (pGuide->cyWriting < GUIDE_MIN_SIZE) || (GUIDE_MAX_SIZE < pGuide->cyWriting) ||
  559. (GUIDE_MAX_SIZE < pGuide->cyMid) ||
  560. (GUIDE_MAX_SIZE < pGuide->cyBase) ||
  561. (pGuide->nDir != HWX_HORIZONTAL)
  562. ) {
  563. SetLastError(ERROR_CAN_NOT_COMPLETE);
  564. return FALSE;
  565. }
  566. // Now that we know the values make some sense, we can check for internal consistancy.
  567. if ((((GUIDE_MAX_COORD - pGuide->xOrigin) / pGuide->cHorzBox) < pGuide->cxBox) ||
  568. (((GUIDE_MAX_COORD - pGuide->yOrigin) / pGuide->cVertBox) < pGuide->cyBox) ||
  569. ((pGuide->cxOffset + pGuide->cxWriting) > pGuide->cxBox) ||
  570. ((pGuide->cyOffset + pGuide->cyWriting) > pGuide->cyBox) ||
  571. // By spec. cyMid and cyBase should be zero, but old code still sets them,
  572. // so we must allow plausable values.
  573. // (pGuide->cyMid != 0 || pGuide->cyBase != 0)
  574. (pGuide->cyMid > pGuide->cyBase || pGuide->cyBase > pGuide->cyWriting)
  575. ) {
  576. SetLastError(ERROR_CAN_NOT_COMPLETE);
  577. return FALSE;
  578. }
  579. SetLatticeGuide(pVRC->pLattice, pGuide);
  580. return TRUE;
  581. }
  582. ////
  583. // HwxALCValid
  584. //
  585. // Tells what character sets to select.
  586. ////
  587. BOOL HwxALCValid(HRC hrc, ALC alc)
  588. {
  589. VRC *pVRC = (VRC *)hrc;
  590. LogMessage("HwxALCValid(%08X,%08X)\n",hrc,alc);
  591. // Check parameters.
  592. if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
  593. SetLastError(ERROR_INVALID_HANDLE);
  594. return FALSE;
  595. }
  596. if (pVRC->fHaveInput || pVRC->fEndInput || pVRC->pLatticePath) {
  597. SetLastError(ERROR_CAN_NOT_COMPLETE);
  598. return FALSE;
  599. }
  600. // Pass the ALC on to the lattice.
  601. SetLatticeALCValid(pVRC->pLattice, alc);
  602. return TRUE;
  603. }
  604. ////
  605. // HwxALCPriority
  606. //
  607. // Tells what character sets to move to the top of the alt list..
  608. ////
  609. BOOL HwxALCPriority(HRC hrc, ALC alc)
  610. {
  611. VRC *pVRC = (VRC *)hrc;
  612. LogMessage("HwxALCPriority(%08X,%08X)\n",hrc,alc);
  613. // Check parameters.
  614. if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
  615. SetLastError(ERROR_INVALID_HANDLE);
  616. return FALSE;
  617. }
  618. if (pVRC->fHaveInput || pVRC->fEndInput || pVRC->pLatticePath) {
  619. SetLastError(ERROR_CAN_NOT_COMPLETE);
  620. return FALSE;
  621. }
  622. // Pass the ALC on to the lattice.
  623. SetLatticeALCPriority(pVRC->pLattice, alc);
  624. return TRUE;
  625. }
  626. ////
  627. // HwxProcess
  628. //
  629. // Process the ink and return the results.
  630. ////
  631. BOOL HwxProcess(HRC hrc)
  632. {
  633. VRC *pVRC = (VRC *)hrc;
  634. LogMessage("HwxProcess(%08X)\n",hrc);
  635. // Did we get a handle? Is it free input?
  636. // Does it have a lattice?
  637. // JBENN: Should it be an error if we have no strokes?
  638. if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
  639. SetLastError(ERROR_INVALID_HANDLE);
  640. return FALSE;
  641. }
  642. // Do any processing we can.
  643. if (!ProcessLattice(pVRC->pLattice, pVRC->fEndInput))
  644. {
  645. return FALSE;
  646. }
  647. #ifdef USE_IFELANG3
  648. // Do we have all the input? Also, make sure we are not in separator mode
  649. if (pVRC->fEndInput && !pVRC->pLattice->fSepMode)
  650. {
  651. // Apply the language model.
  652. #ifdef HWX_TUNE
  653. // If we have tuning enabled, then never call IFELang3 directly.
  654. if (g_pTuneFile == NULL)
  655. #endif
  656. {
  657. ApplyLanguageModel(pVRC->pLattice, NULL);
  658. }
  659. }
  660. #endif
  661. // Free up any previous path
  662. if (pVRC->pLatticePath != NULL) {
  663. FreeLatticePath(pVRC->pLatticePath);
  664. }
  665. // Get path (which may be partial if we have not reached the end of input)
  666. if (!GetCurrentPath(pVRC->pLattice,&pVRC->pLatticePath)) {
  667. SetLastError(ERROR_OUTOFMEMORY);
  668. return FALSE;
  669. }
  670. return TRUE;
  671. }
  672. ////
  673. // HwxEndInput
  674. //
  675. // No more ink is coming (or can be added) once this is called.
  676. ////
  677. BOOL HwxEndInput(HRC hrc)
  678. {
  679. VRC *pVRC = (VRC *)hrc;
  680. LogMessage("HwxEndInput(%08X)\n",hrc);
  681. // Did we get a handle? Is it free input?
  682. // Does it have a lattice?
  683. // JBENN: Should it be an error if we have no strokes?
  684. if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
  685. SetLastError(ERROR_INVALID_HANDLE);
  686. return FALSE;
  687. }
  688. pVRC->fEndInput = TRUE;
  689. return TRUE;
  690. }
  691. ////
  692. // HwxInput
  693. //
  694. // Add Ink to the HRC.
  695. ////
  696. BOOL HwxInput(HRC hrc, POINT *pPnt, UINT cPnt, DWORD dwTick)
  697. {
  698. VRC *pVRC = (VRC *)hrc;
  699. #ifdef DEBUG_LOG_API
  700. int i;
  701. LogMessage("HwxInput(%08X,%d,%d)\n",hrc,cPnt,dwTick);
  702. for (i = 0; i < (int) cPnt; i++)
  703. LogMessage(" xy[%i]=%d,%d\n", i, pPnt[i].x, pPnt[i].y);
  704. #endif
  705. // Did we get a handle? Does it have a lattice?
  706. if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
  707. SetLastError(ERROR_INVALID_HANDLE);
  708. return FALSE;
  709. } else if (pVRC->fEndInput) {
  710. SetLastError(ERROR_CAN_NOT_COMPLETE);
  711. return FALSE;
  712. } else if (!cPnt || pPnt==NULL) {
  713. SetLastError(ERROR_CAN_NOT_COMPLETE);
  714. return FALSE;
  715. }
  716. // Mark as having input.
  717. pVRC->fHaveInput = TRUE;
  718. // Add stroke to the lattice.
  719. if (AddStrokeToLattice(pVRC->pLattice, cPnt, pPnt, dwTick) >= 0) {
  720. return TRUE;
  721. } else {
  722. // JRB: FIXME: Is this always the correct error code?
  723. SetLastError(ERROR_OUTOFMEMORY);
  724. return FALSE;
  725. }
  726. }
  727. ////
  728. // HwxGetResults
  729. //
  730. // Returns the results from the recognizer.
  731. ////
  732. int HwxGetResults(
  733. HRC hrc, // HRC containing results
  734. UINT cAlt, // number of alternates
  735. UINT iFirst, // index of first character to return
  736. UINT cBoxRes, // number of characters to return
  737. HWXRESULTS *rgBoxResults // array of cBoxRes ranked lists
  738. ) {
  739. UINT ii, jj;
  740. VRC *pVRC = (VRC *)hrc;
  741. LATTICE_PATH *pLP;
  742. UINT iBNFirst, iBNLast;
  743. HWXRESULTS *pResults;
  744. int sizeResults;
  745. LogMessage("HwxGetResults(%08X,%d,%d,%d)\n",hrc,cAlt,iFirst,cBoxRes);
  746. // Check parameters.
  747. if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
  748. SetLastError(ERROR_INVALID_HANDLE);
  749. LogMessage(" invalid handle\n");
  750. return -1;
  751. } else if (!rgBoxResults) {
  752. SetLastError(ERROR_CAN_NOT_COMPLETE);
  753. LogMessage(" bad rgBoxResults\n");
  754. return -1;
  755. // } else if (!pVRC->fEndInput) {
  756. // SetLastError(ERROR_CAN_NOT_COMPLETE);
  757. // LogMessage(" not at end of input\n");
  758. // return -1;
  759. } else if (GetLatticeStrokeCount(pVRC->pLattice) < 1) {
  760. // No ink to process.
  761. LogMessage(" no strokes\n");
  762. return 0;
  763. } else if (!pVRC->pLatticePath) {
  764. SetLastError(ERROR_CAN_NOT_COMPLETE);
  765. LogMessage(" no path\n");
  766. return -1;
  767. } else if (rgBoxResults == NULL || 0 == cAlt) {
  768. // Asked for nothing.
  769. LogMessage(" no results requested\n");
  770. return 0;
  771. }
  772. // JBENN: WARNING: This code assumes that the lattice sorts
  773. // the strokes by the box order, so that the box numbers are
  774. // in acending order.
  775. // Find the first box in the requested range.
  776. pLP = pVRC->pLatticePath;
  777. for (iBNFirst = 0; iBNFirst < (UINT)pLP->nChars; ++iBNFirst) {
  778. ASSERT(iBNFirst == 0 || pLP->pElem[iBNFirst - 1].iBoxNum < pLP->pElem[iBNFirst].iBoxNum);
  779. if ((UINT)pLP->pElem[iBNFirst].iBoxNum >= iFirst) {
  780. break;
  781. }
  782. }
  783. if (iBNFirst >= (UINT)pLP->nChars) {
  784. // Nothing found.
  785. LogMessage(" not enough results %d vs %d\n",iBNFirst,pLP->nChars);
  786. return 0;
  787. }
  788. // Compute last box.
  789. iBNLast = iBNFirst + cBoxRes;
  790. if (iBNLast > (UINT)pLP->nChars) {
  791. iBNLast = (UINT)pLP->nChars;
  792. }
  793. // Copy out the results.
  794. pResults = rgBoxResults;
  795. sizeResults = sizeof(HWXRESULTS) + (cAlt - 1) * sizeof(pResults->rgChar[0]);
  796. for (ii = iBNFirst; ii < iBNLast; ++ii) {
  797. UINT cAltUsed;
  798. wchar_t oldTemp, newTemp;
  799. // Retyrn what box was this in.
  800. pResults->indxBox = (short)pLP->pElem[ii].iBoxNum;
  801. // Get the alternates.
  802. cAltUsed = GetAlternatesForCharacterInCurrentPath(
  803. pVRC->pLattice, pVRC->pLatticePath, ii, cAlt, pResults->rgChar
  804. );
  805. // Move top choice to top of list, if it is not already there.
  806. // We slide everything above it down.
  807. oldTemp = pLP->pElem[ii].wChar;
  808. for (jj = 0; jj < cAltUsed; ++jj) {
  809. newTemp = pResults->rgChar[jj];
  810. pResults->rgChar[jj] = oldTemp;
  811. if (newTemp == pLP->pElem[ii].wChar) {
  812. break;
  813. }
  814. oldTemp = newTemp;
  815. }
  816. // Zero any unused slots.
  817. for (jj = cAltUsed; jj < cAlt; ++jj) {
  818. pResults->rgChar[jj] = L'\0';
  819. }
  820. #if 0
  821. {
  822. /*
  823. FILE *f=_wfopen(L"c:/box-results.utf",L"ab");
  824. if (ftell(f)==0) fwprintf(f,L"%c",0xfeff);
  825. for (jj=0; jj<cAltUsed; jj++)
  826. fwprintf(f,L"%c U+%04X ",pResults->rgChar[jj],pResults->rgChar[jj]);
  827. fwprintf(f,L"\r\n");
  828. fclose(f);
  829. */
  830. FILE *f=fopen("c:/box-results.utf","a");
  831. for (jj=0; jj<cAltUsed; jj++)
  832. fprintf(f,"U+%04X ",pResults->rgChar[jj]);
  833. fprintf(f,"\n");
  834. fclose(f);
  835. }
  836. #endif
  837. for (jj=0; jj<cAlt; jj++) {
  838. LogMessage(" Box %d alt %d wch U+%04X\n",ii,jj,pResults->rgChar[jj]);
  839. }
  840. // Sequence to next results structure.
  841. pResults = (HWXRESULTS *)(((BYTE *)pResults) + sizeResults);
  842. }
  843. // Return the number of character positions filled in.
  844. return iBNLast - iBNFirst;
  845. }
  846. ////
  847. // HwxSetContext
  848. //
  849. // Handwriting recognition performance can be improved if the recognizer has context
  850. // information available during processing. Context information is added to an HRC
  851. // via the HwxSetContext function which provides one character of prior context for
  852. // the recognizer. This function should be called prior to using the HwxProcess
  853. // function.
  854. //
  855. // Remarks
  856. // If this function is not called, the recognizer will assume that no prior context
  857. // is available. Performance of the recognizer is improved if context can be
  858. // provided. Currently this function improves performance only for the first
  859. // character in the HRC. If the HRC contains ink for multiple characters,
  860. // the recognition process itself will provide context information for characters
  861. // after the first character, but no context information is available for the first
  862. // character in the HRC unless it is provided via the HwxSetContext function.
  863. // This is especially important for situations where ink are recognized one
  864. // character at a time.
  865. ////
  866. BOOL HwxSetContext(HRC hrc, WCHAR wchContext)
  867. {
  868. VRC *pVRC = (VRC *)hrc;
  869. wchar_t wszBefore[2];
  870. LogMessage("HwxSetContext(%08X,U+%04X)\n",hrc,wchContext);
  871. // Check parameters.
  872. if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
  873. SetLastError(ERROR_INVALID_HANDLE);
  874. return FALSE;
  875. }
  876. if (pVRC->fHaveInput || pVRC->fEndInput || pVRC->pLatticePath) {
  877. SetLastError(ERROR_CAN_NOT_COMPLETE);
  878. return FALSE;
  879. }
  880. wszBefore[0] = wchContext;
  881. wszBefore[1] = 0;
  882. return SetHwxCorrectionContext(hrc, wszBefore, NULL);
  883. }
  884. ////
  885. // HwxResultsAvailable
  886. //
  887. // FIXME: Make this work for partial results!
  888. //
  889. // Warnings: This assumes the writer can't go back and touch up previous
  890. // characters.
  891. //
  892. // Returns the number of characters that can be gotten and displayed safely
  893. // because the viterbi search has folded to one path at this point.
  894. //
  895. // Return the number of characters available in the
  896. // path that are ready to get. This API looks at the viterbi search and
  897. // any characters far enough back in the input that all the paths merge
  898. // to 1 are done and can be displayed because nothing further out in the
  899. // input will change the best path back once it's merged to a single path.
  900. ////
  901. INT HwxResultsAvailable(HRC hrc)
  902. {
  903. VRC *pVRC = (VRC *)hrc;
  904. LogMessage("HwxResultsAvailable(%08X)\n",hrc);
  905. // Check parameters.
  906. if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
  907. SetLastError(ERROR_INVALID_HANDLE);
  908. return -1;
  909. // } else if (!pVRC->fEndInput) {
  910. // SetLastError(ERROR_CAN_NOT_COMPLETE);
  911. // return -1;
  912. } else if (GetLatticeStrokeCount(pVRC->pLattice) < 1) {
  913. // No ink to process.
  914. return 0;
  915. }
  916. // Currently, can't actually do partial results, so if we have
  917. // not finished processing, we return zero!
  918. if (!pVRC->pLatticePath) {
  919. return 0;
  920. }
  921. // OK, we have results, so return the number available.
  922. LogMessage(" %d results\n",pVRC->pLatticePath->nChars);
  923. return pVRC->pLatticePath->nChars;
  924. }
  925. ////
  926. // HwxSetPartial
  927. //
  928. // Set parameters for recognition
  929. ////
  930. BOOL HwxSetPartial(HRC hrc, UINT partialMode)
  931. {
  932. VRC *pVRC = (VRC *)hrc;
  933. LogMessage("HwxSetPartial(%08X,%d)\n",hrc,partialMode);
  934. // Check parameters.
  935. if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
  936. SetLastError(ERROR_INVALID_HANDLE);
  937. return FALSE;
  938. }
  939. if (pVRC->fHaveInput || pVRC->fEndInput || pVRC->pLatticePath) {
  940. SetLastError(ERROR_CAN_NOT_COMPLETE);
  941. return FALSE;
  942. }
  943. if (partialMode > HWX_PARTIAL_FREE) {
  944. SetLastError(ERROR_CAN_NOT_COMPLETE);
  945. return FALSE;
  946. }
  947. pVRC->pLattice->recogSettings.partialMode = partialMode;
  948. return TRUE;
  949. }
  950. ////
  951. // HwxSetAbort
  952. //
  953. // Set address for abort detection
  954. ////
  955. BOOL HwxSetAbort(HRC hrc, UINT *pAbort)
  956. {
  957. VRC *pVRC = (VRC *)hrc;
  958. LogMessage("HwxSetAbort(%08X,%08X)\n",hrc,pAbort);
  959. // Check parameters.
  960. if (!hrc || !pVRC->fBoxedInput || !pVRC->pLattice) {
  961. SetLastError(ERROR_INVALID_HANDLE);
  962. return FALSE;
  963. }
  964. if (pVRC->fHaveInput || pVRC->fEndInput || pVRC->pLatticePath) {
  965. SetLastError(ERROR_CAN_NOT_COMPLETE);
  966. return FALSE;
  967. }
  968. pVRC->pLattice->recogSettings.pAbort = pAbort;
  969. return TRUE;
  970. }