mirror of https://github.com/tongzx/nt5src
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.
5389 lines
153 KiB
5389 lines
153 KiB
// FILE: WispApis.c
|
|
//
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "volcanop.h"
|
|
#include "RecTypes.h"
|
|
#include "RecApis.h"
|
|
|
|
#include <Limits.h>
|
|
|
|
#include <strsafe.h>
|
|
|
|
#include "TpcError.h"
|
|
#include "TpgHandle.h"
|
|
|
|
#include "res.h"
|
|
|
|
//#define ENABLE_CONFIDENCE_LEVEL
|
|
|
|
#define LARGE_BREAKS 500
|
|
|
|
// definitions of possible handle types that WISP used
|
|
#define TPG_HRECOCONTEXT (1)
|
|
#define TPG_HRECOGNIZER (2)
|
|
#define TPG_HRECOALT (3)
|
|
|
|
//Why cannot we include penwin.h???? I have to redefine everything I need....
|
|
#define SYV_UNKNOWN 0x00000001L
|
|
BOOL SymbolToCharacterW(SYV *pSyv, int cSyv, WCHAR *wsz, int *pConv);
|
|
|
|
// If this pointer is non-NULL, then free input is enabled.
|
|
extern BBOX_PROB_TABLE *g_pProbTable;
|
|
|
|
// String identifying which language is loaded
|
|
extern wchar_t *g_szRecognizerLanguage;
|
|
|
|
extern HINSTANCE g_hInstanceDllCode;
|
|
|
|
#define NUMBER_OF_ALTERNATES 10
|
|
#define TAB_STROKE_INC 30
|
|
|
|
// {7DFE11A7-FB5D-4958-8765-154ADF0D833F}
|
|
static const GUID GUID_CONFIDENCELEVEL =
|
|
{ 0x7dfe11a7, 0xfb5d, 0x4958, { 0x87, 0x65, 0x15, 0x4a, 0xdf, 0x0d, 0x83, 0x3f } };
|
|
|
|
// {8CC24B27-30A9-4b96-9056-2D3A90DA0727}
|
|
static const GUID GUID_LINEMETRICS =
|
|
{ 0x8cc24b27, 0x30a9, 0x4b96, { 0x90, 0x56, 0x2d, 0x3a, 0x90, 0xda, 0x07, 0x27 } };
|
|
|
|
// {6D4087D7-61D2-495f-9293-5B7B1C3FCEAB}
|
|
static const CLSID JPN_CLSID =
|
|
{ 0x6D4087D7, 0x61D2, 0x495f, { 0x92, 0x93, 0x5B, 0x7B, 0x1C, 0x3F, 0xCE, 0xAB } };
|
|
|
|
static const CLSID KOR_CLSID =
|
|
{ 0x6D5087D7, 0x61D2, 0x495f, { 0x92, 0x93, 0x5B, 0x7B, 0x1C, 0x3F, 0xCE, 0xAB } };
|
|
|
|
static const CLSID CHS_CLSID =
|
|
{ 0x6D6087D7, 0x61D2, 0x495f, { 0x92, 0x93, 0x5B, 0x7B, 0x1C, 0x3F, 0xCE, 0xAB } };
|
|
|
|
static const CLSID CHT_CLSID =
|
|
{ 0x6D7087D7, 0x61D2, 0x495f, { 0x92, 0x93, 0x5B, 0x7B, 0x1C, 0x3F, 0xCE, 0xAB } };
|
|
|
|
//
|
|
// Definitions of strucure which pointers are used
|
|
// to define the WISP handles (HRECOGNIZER, HRECOCONTEXT
|
|
// HRECOALTERNATE)
|
|
////////////////////////////////////////////////////////
|
|
|
|
|
|
// This is the structure used for the WISP recognizer
|
|
// There is no data, because we have nothing to store
|
|
struct WispRec
|
|
{
|
|
long unused;
|
|
};
|
|
|
|
// This is the structure for WISP alternates
|
|
// It contains an array of column used in the
|
|
// lattice and an array of indexes used in
|
|
// those columns.
|
|
// We alse cache the reco context for which
|
|
// this alternate is valis, the length of the
|
|
// string this alternate corresponds to and
|
|
// the original RECO_RANGE this alternate was
|
|
// produced from (in a call to GetAlternateList
|
|
// or other)
|
|
struct WispAlternate
|
|
{
|
|
HRECOCONTEXT hrc;
|
|
int *pIndexInColumn;
|
|
int *pColumnIndex;
|
|
int iNumberOfColumns;
|
|
int iLength;
|
|
RECO_RANGE OriginalRecoRange;
|
|
};
|
|
|
|
// This is the WISP structure for the reco context.
|
|
// It contains information on the guide used, the
|
|
// CAC modem the number of strokes currently
|
|
// entered, the context (prefix)
|
|
// It also contains the handle to the HWX reco
|
|
// context
|
|
// We store the lattice so that we not need to
|
|
// recreate it every time we are asked for it
|
|
struct WispContext
|
|
{
|
|
HRC hrc;
|
|
RECO_GUIDE *pGuide;
|
|
ULONG uiGuideIndex;
|
|
BOOL bIsBoxed;
|
|
BOOL bIsCAC;
|
|
BOOL bCACEndInk;
|
|
ULONG iCACMode;
|
|
UINT uAbort;
|
|
ULONG ulCurrentStrokeCount;
|
|
BOOL bHasTextContext; // Whether any context has been set
|
|
WCHAR *wszBefore; // Context before ink
|
|
WCHAR *wszAfter; // Context after ink
|
|
DWORD dwFlags; // Flags
|
|
WCHAR *wszFactoid; // Factoid
|
|
|
|
// Lattice for the automation code, with associated data structures
|
|
RECO_LATTICE *pLattice;
|
|
RECO_LATTICE_PROPERTY *pLatticeProperties;
|
|
BYTE *pLatticePropertyValues;
|
|
RECO_LATTICE_PROPERTY **ppLatticeProperties;
|
|
};
|
|
|
|
// Structure for the alternate list recursive call
|
|
////////////////////////////////////////////////
|
|
typedef struct tagAltRank
|
|
{
|
|
struct WispAlternate *wispalt;
|
|
FLOAT fScore;
|
|
struct tagAltRank *next;
|
|
BOOL bCurrentPath;
|
|
} AltRank;
|
|
|
|
typedef struct tagAltRankList
|
|
{
|
|
AltRank *pFirst;
|
|
AltRank *pLast;
|
|
ULONG ulSize;
|
|
} AltRankList;
|
|
typedef struct tagDiffBreakElement
|
|
{
|
|
int iColumn;
|
|
int iIndex;
|
|
struct tagDiffBreakElement *pNext;
|
|
} DiffBreakElement;
|
|
typedef struct tagDifBreakList
|
|
{
|
|
int iColumnCount;
|
|
DiffBreakElement *pFirst;
|
|
float score;
|
|
BOOL bCurrentPath;
|
|
} DifBreakList;
|
|
|
|
typedef struct tagDifBreakAltStruct
|
|
{
|
|
VRC *vrc; // the recognizer data structure
|
|
int iFirstStroke; // the first stroke in the original alternate
|
|
ULONG ulMax; // Max alternates that we want to return
|
|
int iLastChar; // This is to put in the original reco range of the alternate
|
|
int iFirstChar; // This is to put in the original reco range of the alternate
|
|
AltRankList *paltRankList; // List of Alternates
|
|
int iMode; // Segmentation mode (DIFF_BREAK, ...)
|
|
} DifBreakAltStruct;
|
|
|
|
/////////////////////////////////////////////////////
|
|
// Declare the GUIDs and consts of the Packet description
|
|
/////////////////////////////////////////////////////
|
|
const GUID g_guidx ={ 0x598a6a8f, 0x52c0, 0x4ba0, { 0x93, 0xaf, 0xaf, 0x35, 0x74, 0x11, 0xa5, 0x61 } };
|
|
const GUID g_guidy = { 0xb53f9f75, 0x04e0, 0x4498, { 0xa7, 0xee, 0xc3, 0x0d, 0xbb, 0x5a, 0x90, 0x11 } };
|
|
const PROPERTY_METRICS g_DefaultPropMetrics = { LONG_MIN, LONG_MAX, PROPERTY_UNITS_DEFAULT, 1.0 };
|
|
|
|
/////////////////////////////////////////////////////
|
|
// Helper function to bubble sort an array
|
|
/////////////////////////////////////////////////////
|
|
// I use a bubble sort because most likely if the
|
|
// array is not already sorted, we probably have one or
|
|
// two inversion. This is caused by the fact that
|
|
// people usually write the letters in a word in
|
|
// the correct order
|
|
BOOL SlowSort(ULONG *pTab, ULONG ulSize)
|
|
{
|
|
ULONG i, j, temp;
|
|
BOOL bPermut;
|
|
// Stupid bubble sort
|
|
for (i = 0; i<ulSize; i++)
|
|
{
|
|
bPermut = FALSE;
|
|
for (j = 0; j < ulSize-1-i; j++)
|
|
{
|
|
if (pTab[j] > pTab[j+1])
|
|
{
|
|
bPermut = TRUE;
|
|
temp = pTab[j];
|
|
pTab[j] = pTab[j+1];
|
|
pTab[j+1] = temp;
|
|
}
|
|
}
|
|
if (!bPermut) return TRUE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
// Implementation of the Wisp Reco Apis
|
|
/////////////////////////////////////////////////////
|
|
|
|
|
|
// CreateRecognizer
|
|
// Returns a recognizer handle to the recognizer
|
|
// corresponding to the passed CLSID. In the case
|
|
// of this dll, we only support one CLSID so we will
|
|
// not even check for the value of the clsid
|
|
// (even if the clsid is null)
|
|
//
|
|
// Parameter:
|
|
// pCLSID [in] : The pointer to the CLSID
|
|
// that determines what recognizer we want
|
|
// phrec [out] : The address of the returned recognizer
|
|
// handle.
|
|
//////////////////////////////////////////////////////////////////////
|
|
HRESULT WINAPI CreateRecognizer(CLSID *pCLSID, HRECOGNIZER *phrec)
|
|
{
|
|
struct WispRec *pRec;
|
|
|
|
// We might want to make NULL illegal later.
|
|
if (pCLSID != NULL && IsBadReadPtr(pCLSID, sizeof(CLSID)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// validate the pointer
|
|
if (IsBadWritePtr(phrec, sizeof(HRECOGNIZER)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// initialize the east asian recognizers
|
|
#ifdef USE_RESOURCES
|
|
if (!HwxConfig())
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
#endif
|
|
|
|
// We might want to make NULL illegal later.
|
|
if (pCLSID != NULL)
|
|
{
|
|
if (wcscmp(g_szRecognizerLanguage, L"JPN") == 0 &&
|
|
!IsEqualCLSID(pCLSID, &JPN_CLSID))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (wcscmp(g_szRecognizerLanguage, L"CHS") == 0 &&
|
|
!IsEqualCLSID(pCLSID, &CHS_CLSID))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (wcscmp(g_szRecognizerLanguage, L"CHT") == 0 &&
|
|
!IsEqualCLSID(pCLSID, &CHT_CLSID))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (wcscmp(g_szRecognizerLanguage, L"KOR") == 0 &&
|
|
!IsEqualCLSID(pCLSID, &KOR_CLSID))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
// We only have one CLSID per recognizer so always return an hrec...
|
|
pRec = (struct WispRec*)ExternAlloc(sizeof(*pRec));
|
|
if (NULL == pRec)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
(*phrec) = (HRECOGNIZER)CreateTpgHandle(TPG_HRECOGNIZER, pRec);
|
|
if (0 == (*phrec))
|
|
{
|
|
ExternFree(pRec);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// DestroyRecognizer
|
|
// Destroys a recognizer handle. Free the associate memory
|
|
//
|
|
// Parameter:
|
|
// hrec [in] : handle to the recognizer
|
|
/////////////////////////////////////////////////////////////
|
|
HRESULT WINAPI DestroyRecognizer(HRECOGNIZER hrec)
|
|
{
|
|
struct WispRec *pRec;
|
|
|
|
// destroy the handle and return the corresponding pointer
|
|
pRec = (struct WispRec*)DestroyTpgHandle((HANDLE)hrec, TPG_HRECOGNIZER);
|
|
if (NULL == pRec)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
#ifdef USE_RESOURCES
|
|
if (!HwxUnconfig(TRUE))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
#endif
|
|
|
|
ExternFree(pRec);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// GetRecoAttributes
|
|
// This function returns the reco attributes corresponding
|
|
// to a given recognizer. Since we only have one recognizer
|
|
// type we always return the same things.
|
|
//
|
|
// Parameters:
|
|
// hrc [in] : The handle to the recognizer we want the
|
|
// the attributes for.
|
|
// pRecoAttrs [out] : Address of the user allocated buffer
|
|
// to hold the reco attributes.
|
|
///////////////////////////////////////////////////////////////////////////
|
|
HRESULT WINAPI GetRecoAttributes(HRECOGNIZER hrec, RECO_ATTRS* pRecoAttrs)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HRSRC hrsrc = NULL;
|
|
HGLOBAL hg = NULL;
|
|
LPBYTE pv = NULL;
|
|
WORD wCurrentCount = 0;
|
|
WORD wRecognizerCount = 0;
|
|
DWORD dwRecoCapa;
|
|
WORD wLanguageCount;
|
|
WORD *aLanguages;
|
|
WORD iLang;
|
|
struct WispRec *pRec;
|
|
|
|
if (IsBadWritePtr(pRecoAttrs, sizeof(RECO_ATTRS)))
|
|
return E_POINTER;
|
|
|
|
// Check the recognizer handle
|
|
pRec = (struct WispRec*)FindTpgHandle((HANDLE)hrec, TPG_HRECOGNIZER);
|
|
if (NULL == pRec)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ZeroMemory(pRecoAttrs, sizeof(RECO_ATTRS));
|
|
|
|
// Update the global structure is necessary
|
|
// Load the resources
|
|
// Load the recognizer friendly name
|
|
if (0 == LoadStringW(g_hInstanceDllCode, // handle to resource module
|
|
RESID_WISP_FRIENDLYNAME, // resource identifier
|
|
pRecoAttrs->awcFriendlyName, // resource buffer
|
|
sizeof(pRecoAttrs->awcFriendlyName) / sizeof(WCHAR) // size of buffer
|
|
))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
// Load the recognizer vendor name
|
|
if (0 == LoadStringW(g_hInstanceDllCode, // handle to resource module
|
|
RESID_WISP_VENDORNAME, // resource identifier
|
|
pRecoAttrs->awcVendorName, // resource buffer
|
|
sizeof(pRecoAttrs->awcVendorName) / sizeof(WCHAR) // size of buffer
|
|
))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hrsrc = FindResource(g_hInstanceDllCode, // module handle
|
|
(LPCTSTR)RESID_WISP_DATA, // resource name
|
|
(LPCTSTR)RT_RCDATA // resource type
|
|
);
|
|
if (NULL == hrsrc)
|
|
{
|
|
// The resource is not found!
|
|
ASSERT(NULL != hrsrc);
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hg = LoadResource(
|
|
g_hInstanceDllCode, // module handle
|
|
hrsrc // resource handle
|
|
);
|
|
if (NULL == hg)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pv = (LPBYTE)LockResource(
|
|
hg // handle to resource
|
|
);
|
|
if (NULL == pv)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
dwRecoCapa = *((DWORD*)pv);
|
|
pv += sizeof(dwRecoCapa);
|
|
wLanguageCount = *((WORD*)pv);
|
|
pv += sizeof(wLanguageCount);
|
|
aLanguages = (WORD*)pv;
|
|
pv += wLanguageCount * sizeof(WORD);
|
|
|
|
|
|
// Fill the reco attricute structure for this recognizer
|
|
// Add the languages
|
|
ASSERT(wLanguageCount < 64);
|
|
for (iLang = 0; iLang < wLanguageCount; iLang++)
|
|
{
|
|
pRecoAttrs->awLanguageId[iLang] = aLanguages[iLang];
|
|
}
|
|
// End the list with a NULL
|
|
pRecoAttrs->awLanguageId[wLanguageCount] = 0;
|
|
// Add the recocapability flag
|
|
pRecoAttrs->dwRecoCapabilityFlags = dwRecoCapa;
|
|
|
|
return hr;
|
|
}
|
|
|
|
// CreateRecoContext
|
|
// This function creates a reco context for a given recognizer
|
|
// Since we only have one type of recognizers in this dll,
|
|
// always return the same kind of reco context.
|
|
//
|
|
// Parameters:
|
|
// hrec [in] : Handle to the recognizer we want to create a
|
|
// reco context for.
|
|
// phrc [out] : Pointer to the returned reco context's handle
|
|
////////////////////////////////////////////////////////////////////////
|
|
HRESULT WINAPI CreateContext(HRECOGNIZER hrec, HRECOCONTEXT *phrc)
|
|
{
|
|
struct WispContext *pWispContext = NULL;
|
|
|
|
struct WispRec *pRec;
|
|
|
|
// Check the recognizer handle
|
|
pRec = (struct WispRec*)FindTpgHandle((HANDLE)hrec, TPG_HRECOGNIZER);
|
|
if (NULL == pRec)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// validate the pointer
|
|
if (IsBadWritePtr(phrc, sizeof(HRECOCONTEXT)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
pWispContext = (struct WispContext*)ExternAlloc(sizeof(struct WispContext));
|
|
if (!pWispContext)
|
|
return E_OUTOFMEMORY;
|
|
|
|
pWispContext->pGuide = NULL;
|
|
pWispContext->pLattice = NULL;
|
|
pWispContext->pLatticeProperties = NULL;
|
|
pWispContext->pLatticePropertyValues = NULL;
|
|
pWispContext->ppLatticeProperties = NULL;
|
|
pWispContext->bIsBoxed = FALSE;
|
|
pWispContext->bIsCAC = FALSE;
|
|
pWispContext->iCACMode = CAC_FULL;
|
|
pWispContext->bCACEndInk = FALSE;
|
|
pWispContext->uAbort = 0;
|
|
pWispContext->ulCurrentStrokeCount = 0;
|
|
pWispContext->hrc = NULL;
|
|
pWispContext->bHasTextContext = FALSE;
|
|
pWispContext->wszBefore = NULL;
|
|
pWispContext->wszAfter = NULL;
|
|
pWispContext->dwFlags = 0;
|
|
pWispContext->wszFactoid = NULL;
|
|
|
|
// create the handle
|
|
*phrc = (HRECOCONTEXT)CreateTpgHandle(TPG_HRECOCONTEXT, pWispContext);
|
|
if (NULL == (*phrc))
|
|
{
|
|
ExternFree(pWispContext);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// creates an HRC by calling the appropriate hwx api
|
|
HRESULT CreateHRCinContext(struct WispContext *pWispContext)
|
|
{
|
|
// are we in boxed mode
|
|
if (pWispContext->bIsBoxed)
|
|
{
|
|
pWispContext->hrc = HwxCreate(NULL);
|
|
}
|
|
// free
|
|
else
|
|
{
|
|
pWispContext->hrc = CreateCompatibleHRC(NULL, NULL);
|
|
}
|
|
|
|
// we failed
|
|
if (pWispContext->hrc == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
// reco settings
|
|
if (pWispContext->bHasTextContext)
|
|
{
|
|
if (!SetHwxCorrectionContext (pWispContext->hrc, pWispContext->wszBefore, pWispContext->wszAfter))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
if (!SetHwxFlags(pWispContext->hrc, pWispContext->dwFlags))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
switch (SetHwxFactoid(pWispContext->hrc, pWispContext->wszFactoid))
|
|
{
|
|
case HRCR_OK:
|
|
break;
|
|
|
|
case HRCR_UNSUPPORTED:
|
|
HwxDestroy(pWispContext->hrc);
|
|
return TPC_E_INVALID_PROPERTY;
|
|
|
|
case HRCR_CONFLICT:
|
|
HwxDestroy(pWispContext->hrc);
|
|
return TPC_E_OUT_OF_ORDER_CALL;
|
|
|
|
case HRCR_ERROR:
|
|
default:
|
|
HwxDestroy(pWispContext->hrc);
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Frees a reco lattice
|
|
//
|
|
HRESULT FreeRecoLattice(struct WispContext *wisphrc)
|
|
{
|
|
ULONG i = 0;
|
|
RECO_LATTICE *pRecoLattice = wisphrc->pLattice;
|
|
|
|
if (pRecoLattice == NULL)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// Free the Lattice column information
|
|
if (pRecoLattice->pLatticeColumns)
|
|
{
|
|
// Free the array of strokes
|
|
if (pRecoLattice->pLatticeColumns[0].pStrokes)
|
|
{
|
|
ExternFree(pRecoLattice->pLatticeColumns[0].pStrokes);
|
|
}
|
|
|
|
for (i = 0; i < pRecoLattice->ulColumnCount; i++)
|
|
{
|
|
if (pRecoLattice->pLatticeColumns[i].cpProp.apProps)
|
|
{
|
|
ExternFree(pRecoLattice->pLatticeColumns[i].cpProp.apProps);
|
|
}
|
|
}
|
|
|
|
// Free the array of lattice elements
|
|
if (pRecoLattice->pLatticeColumns[0].pLatticeElements)
|
|
{
|
|
ExternFree(pRecoLattice->pLatticeColumns[0].pLatticeElements);
|
|
}
|
|
|
|
ExternFree(pRecoLattice->pLatticeColumns);
|
|
}
|
|
|
|
// Free the the RecoLattice properties
|
|
if (pRecoLattice->pGuidProperties)
|
|
{
|
|
ExternFree(pRecoLattice->pGuidProperties);
|
|
}
|
|
|
|
// Free the best result information
|
|
if (pRecoLattice->pulBestResultColumns)
|
|
{
|
|
ExternFree(pRecoLattice->pulBestResultColumns);
|
|
}
|
|
|
|
if (pRecoLattice->pulBestResultIndexes)
|
|
{
|
|
ExternFree(pRecoLattice->pulBestResultIndexes);
|
|
}
|
|
|
|
if (wisphrc->pLatticeProperties)
|
|
{
|
|
ExternFree(wisphrc->pLatticeProperties);
|
|
wisphrc->pLatticeProperties = NULL;
|
|
}
|
|
|
|
if (wisphrc->pLatticePropertyValues)
|
|
{
|
|
ExternFree(wisphrc->pLatticePropertyValues);
|
|
wisphrc->pLatticePropertyValues = NULL;
|
|
}
|
|
|
|
if (wisphrc->ppLatticeProperties != NULL)
|
|
{
|
|
ExternFree(wisphrc->ppLatticeProperties);
|
|
wisphrc->ppLatticeProperties = NULL;
|
|
}
|
|
|
|
// Free the RECO_LATTICE structure
|
|
ExternFree(wisphrc->pLattice);
|
|
wisphrc->pLattice = NULL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// DestroyContextInternal
|
|
// Destroy a reco context and free the associated memory.
|
|
//
|
|
// Parameters:
|
|
// hrc [in] : pointer to the reco context to destroy
|
|
//////////////////////////////////////////////////////////////
|
|
HRESULT WINAPI DestroyContextInternal(struct WispContext *wisphrc)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// validate and destroy the handle & return the pointer
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// free the contents of the context
|
|
if (wisphrc->hrc)
|
|
{
|
|
if (wisphrc->bIsBoxed)
|
|
HwxDestroy(wisphrc->hrc);
|
|
else
|
|
DestroyHRC(wisphrc->hrc);
|
|
}
|
|
|
|
if (wisphrc->pGuide)
|
|
{
|
|
ExternFree(wisphrc->pGuide);
|
|
}
|
|
|
|
if (wisphrc->pLattice)
|
|
{
|
|
hr = FreeRecoLattice(wisphrc);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
wisphrc->pLattice = NULL;
|
|
|
|
if (wisphrc->bHasTextContext)
|
|
{
|
|
ExternFree(wisphrc->wszBefore);
|
|
ExternFree(wisphrc->wszAfter);
|
|
}
|
|
|
|
ExternFree(wisphrc->wszFactoid);
|
|
ExternFree(wisphrc);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// DestroyContext
|
|
// Destroy a reco context and free the associated memory.
|
|
//
|
|
// Parameters:
|
|
// hrc [in] : handle to the reco context to destroy
|
|
//////////////////////////////////////////////////////////////
|
|
HRESULT WINAPI DestroyContext(HRECOCONTEXT hrc)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
|
|
// validate and destroy the handle & return the pointer
|
|
wisphrc = (struct WispContext*)DestroyTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
return DestroyContextInternal(wisphrc);
|
|
}
|
|
|
|
#ifdef ENABLE_CONFIDENCE_LEVEL
|
|
|
|
const ULONG PROPERTIES_COUNT = 2;
|
|
|
|
#else
|
|
|
|
const ULONG PROPERTIES_COUNT = 1;
|
|
|
|
#endif
|
|
|
|
// IRecognizer::GetResultPropertyList
|
|
HRESULT WINAPI GetResultPropertyList(HRECOGNIZER hrec, ULONG* pPropertyCount, GUID* pPropertyGuid)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
struct WispRec *pRec;
|
|
|
|
// Check the recognizer handle
|
|
pRec = (struct WispRec*)FindTpgHandle((HANDLE)hrec, TPG_HRECOGNIZER);
|
|
if (NULL == pRec)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadWritePtr(pPropertyCount, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (!pPropertyGuid)
|
|
{
|
|
*pPropertyCount = PROPERTIES_COUNT; // For now we support only two GUID properties
|
|
}
|
|
else
|
|
{
|
|
// Check the array
|
|
if (PROPERTIES_COUNT > *pPropertyCount)
|
|
{
|
|
return TPC_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
if (IsBadWritePtr(pPropertyGuid, sizeof(GUID)*(*pPropertyCount)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
pPropertyGuid[0] = GUID_LINEMETRICS;
|
|
#ifdef ENABLE_CONFIDENCE_LEVEL
|
|
pPropertyGuid[1] = GUID_CONFIDENCELEVEL;
|
|
#endif
|
|
*pPropertyCount = PROPERTIES_COUNT;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// GetPreferredPacketDescription
|
|
// Returns the preferred packet description for the recognizer
|
|
// This is going to be x, y only for this recognizer
|
|
//
|
|
// Parameters:
|
|
// hrec [in] : The recognizer we want the preferred
|
|
// packet description for
|
|
// pPacketDescription [out] : The packet description
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT WINAPI GetPreferredPacketDescription(HRECOGNIZER hrec , PACKET_DESCRIPTION* pPacketDescription)
|
|
{
|
|
struct WispRec *pRec;
|
|
|
|
// Check the recognizer handle
|
|
pRec = (struct WispRec*)FindTpgHandle((HANDLE)hrec, TPG_HRECOGNIZER);
|
|
if (NULL == pRec)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// validate the pointer
|
|
if (IsBadWritePtr(pPacketDescription, sizeof(PACKET_DESCRIPTION)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// We can be called the first time with pPacketProperies
|
|
// equal to NULL, just to get the size of those buffer
|
|
// The second time we get called thoses buffers are allocated, so
|
|
// we can fill them with the data.
|
|
if (pPacketDescription->pPacketProperties)
|
|
{
|
|
// Make sure that the pPacketProperties is of a valid size
|
|
// Set the packet size to the size of x and y
|
|
pPacketDescription->cbPacketSize = 2 * sizeof(LONG);
|
|
|
|
// We are only setting 2 properties (X and Y)
|
|
if (pPacketDescription->cPacketProperties < 2)
|
|
return TPC_E_INSUFFICIENT_BUFFER;
|
|
pPacketDescription->cPacketProperties = 2;
|
|
|
|
// We are not setting buttons
|
|
pPacketDescription->cButtons = 0;
|
|
|
|
// Make sure that the pPacketProperties is of a valid size
|
|
if (IsBadWritePtr(pPacketDescription->pPacketProperties, 2 * sizeof(PACKET_PROPERTY)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// Fill in pPacketProperies
|
|
// Add the GUID_X
|
|
pPacketDescription->pPacketProperties[0].guid = g_guidx;
|
|
pPacketDescription->pPacketProperties[0].PropertyMetrics = g_DefaultPropMetrics;
|
|
|
|
// Add the GUID_Y
|
|
pPacketDescription->pPacketProperties[1].guid = g_guidy;
|
|
pPacketDescription->pPacketProperties[1].PropertyMetrics = g_DefaultPropMetrics;
|
|
}
|
|
else
|
|
{
|
|
// Just fill in the PacketDescription structure leavin NULL
|
|
// pointers for the pguidButtons and pPacketProperies
|
|
|
|
// Set the packet size to the size of x and y
|
|
pPacketDescription->cbPacketSize = 2*sizeof(LONG);
|
|
|
|
// We are only setting 2 properties (X and Y)
|
|
pPacketDescription->cPacketProperties = 2;
|
|
|
|
// We are not setting buttons
|
|
pPacketDescription->cButtons = 0;
|
|
|
|
// There are not guid buttons
|
|
pPacketDescription->pguidButtons = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#define FUZZ_GEN (1e-9) // general fuzz - nine decimal digits
|
|
|
|
/**********************************************************************/
|
|
// Convert double to int
|
|
int RealToInt(double dbl)
|
|
{
|
|
// Add in the rounding threshold.
|
|
// NOTE: The MAXWORD bias used in the floor function
|
|
// below must not be combined with this line. If it
|
|
// is combined the effect of FUZZ_GEN will be lost.
|
|
dbl += 0.5 + FUZZ_GEN;
|
|
|
|
// Truncate
|
|
// The UINT_MAX bias in the floor function will cause
|
|
// truncation (rounding toward minuse infinity) within
|
|
// the range of a short.
|
|
dbl = floor(dbl + UINT_MAX) - UINT_MAX;
|
|
|
|
// Clip the result.
|
|
return dbl > INT_MAX - 7 ? INT_MAX - 7 :
|
|
dbl < INT_MIN + 7 ? INT_MIN + 7 : (int)dbl;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
// Transform POINT array in place
|
|
void Transform(const XFORM *pXf, POINT * pPoints, ULONG cPoints)
|
|
{
|
|
ULONG iPoint = 0;
|
|
LONG xp = 0;
|
|
|
|
if(NULL != pXf)
|
|
{
|
|
for(iPoint = 0; iPoint < cPoints; ++iPoint)
|
|
{
|
|
xp = RealToInt(pPoints[iPoint].x * pXf->eM11 +
|
|
pPoints[iPoint].y * pXf->eM21 + pXf->eDx);
|
|
|
|
pPoints[iPoint].y = RealToInt(pPoints[iPoint].x * pXf->eM12 +
|
|
pPoints[iPoint].y * pXf->eM22 + pXf->eDy);
|
|
|
|
pPoints[iPoint].x = xp;
|
|
}
|
|
}
|
|
}
|
|
|
|
HRESULT WINAPI AddStroke(HRECOCONTEXT hrc, const PACKET_DESCRIPTION* pPacketDesc, ULONG cbPacket, const BYTE *pPacket, const XFORM *pXForm)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG ulPointCount = 0;
|
|
STROKEINFO stInfo;
|
|
POINT *ptArray = NULL;
|
|
struct WispContext *wisphrc;
|
|
ULONG ulXIndex = 0, ulYIndex = 0;
|
|
BOOL bXFound = FALSE, bYFound = FALSE;
|
|
ULONG ulPropIndex = 0;
|
|
ULONG index = 0;
|
|
int hres = 0;
|
|
VRC *vrc = NULL;
|
|
const LONG* pLongs = (const LONG *)(pPacket);
|
|
int temp = 0;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (pPacketDesc != NULL && IsBadReadPtr(pPacketDesc, sizeof(PACKET_DESCRIPTION)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (pXForm != NULL && IsBadReadPtr(pXForm, sizeof(XFORM)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// validate the data pointer
|
|
if(IsBadReadPtr(pPacket, cbPacket))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (!wisphrc->hrc)
|
|
{
|
|
// If we have a free guide and this is not allowed by
|
|
// the recognizer, then fail. Return an out of order
|
|
// error because it probably means they forgot to
|
|
// set the guide before adding ink.
|
|
if (g_pProbTable == NULL && !wisphrc->bIsBoxed)
|
|
{
|
|
return TPC_E_OUT_OF_ORDER_CALL;
|
|
}
|
|
hr = CreateHRCinContext(wisphrc);
|
|
if (FAILED(hr))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
if (wisphrc->bCACEndInk)
|
|
{
|
|
hr = SetCACMode(hrc, wisphrc->iCACMode);
|
|
if (FAILED(hr))
|
|
return E_FAIL;
|
|
}
|
|
|
|
vrc = (VRC*)wisphrc->hrc;
|
|
|
|
// Get the number of packets
|
|
if (pPacketDesc)
|
|
{
|
|
ASSERT(!(cbPacket%(pPacketDesc->cbPacketSize)));
|
|
ulPointCount = (cbPacket)/(pPacketDesc->cbPacketSize);
|
|
}
|
|
else
|
|
{
|
|
ulPointCount = (cbPacket)/(2*sizeof(LONG));
|
|
}
|
|
|
|
// Fill in the stroke info stucture
|
|
// Should check it does not exceed the size of a UINT
|
|
stInfo.cPnt = ulPointCount;
|
|
// PLEASE FIND ANOTHER WAY TO STORE THE STROKE INDEX!!!
|
|
stInfo.dwTick = wisphrc->ulCurrentStrokeCount*60*1000;
|
|
stInfo.wPdk = 0x0001;
|
|
stInfo.cbPnts = ulPointCount*sizeof(POINT);
|
|
|
|
wisphrc->ulCurrentStrokeCount++;
|
|
|
|
// Find the index of GUID_X and GUID_Y
|
|
if (pPacketDesc)
|
|
{
|
|
for (ulPropIndex = 0; ulPropIndex < pPacketDesc->cPacketProperties; ulPropIndex++)
|
|
{
|
|
if (IsEqualGUID(&(pPacketDesc->pPacketProperties[ulPropIndex].guid), &g_guidx))
|
|
{
|
|
bXFound = TRUE;
|
|
ulXIndex = ulPropIndex;
|
|
}
|
|
else
|
|
if (IsEqualGUID(&(pPacketDesc->pPacketProperties[ulPropIndex].guid), &g_guidy))
|
|
{
|
|
bYFound = TRUE;
|
|
ulYIndex = ulPropIndex;
|
|
}
|
|
|
|
if (bXFound && bYFound)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bXFound || !bYFound)
|
|
{
|
|
// The coordinates are not part of the packet!
|
|
// Remove the last stroke from the stroke array
|
|
wisphrc->ulCurrentStrokeCount--;
|
|
return TPC_E_INVALID_PACKET_DESCRIPTION;
|
|
}
|
|
|
|
// Allocate the memory for the stroke
|
|
// Do it very poorly first (we could reuse the buffer)
|
|
ptArray = (POINT*)ExternAlloc(ulPointCount*sizeof(POINT));
|
|
if (!ptArray)
|
|
{
|
|
// Remove the last stroke from the stroke array
|
|
wisphrc->ulCurrentStrokeCount--;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Get the points from the packets
|
|
for (index = 0; index < ulPointCount; index++, pLongs += (pPacketDesc->cbPacketSize)/sizeof(long))
|
|
{
|
|
// Feed the ptArray (array of points)
|
|
ptArray[index].x = *(pLongs+ulXIndex);
|
|
ptArray[index].y = *(pLongs+ulYIndex);
|
|
}
|
|
|
|
|
|
// TO DO, for now I transform the points so they
|
|
// they are in the ink coordinates. It is up to
|
|
// the recognizer team to decide what they should
|
|
// use: raw ink or transformed ink
|
|
Transform(pXForm, ptArray, ulPointCount);
|
|
|
|
if (wisphrc->bIsBoxed)
|
|
{
|
|
if (HwxInput(wisphrc->hrc, ptArray, stInfo.cPnt, stInfo.dwTick))
|
|
hres = HRCR_OK;
|
|
else
|
|
hres = HRCR_ERROR;
|
|
}
|
|
else
|
|
{
|
|
hres = AddPenInputHRC(wisphrc->hrc, ptArray, NULL, 0, &stInfo);
|
|
}
|
|
|
|
if ( hres != HRCR_OK)
|
|
{
|
|
hr = E_FAIL;
|
|
// Remove the last stroke from the stroke array
|
|
wisphrc->ulCurrentStrokeCount--;
|
|
ExternFree(ptArray);
|
|
return hr;
|
|
}
|
|
|
|
ExternFree(ptArray);
|
|
temp = vrc->pLattice->nRealStrokes;
|
|
InterlockedExchange(&(wisphrc->uAbort), temp);
|
|
}
|
|
else
|
|
{
|
|
if (wisphrc->bIsBoxed)
|
|
{
|
|
if (HwxInput(wisphrc->hrc, (POINT*)pPacket, stInfo.cPnt, stInfo.dwTick))
|
|
hres = HRCR_OK;
|
|
else
|
|
hres = HRCR_ERROR;
|
|
}
|
|
else
|
|
{
|
|
hres = AddPenInputHRC(wisphrc->hrc, (POINT*)pPacket, NULL, 0, &stInfo);
|
|
}
|
|
if (hres != HRCR_OK)
|
|
{
|
|
hr = E_FAIL;
|
|
// Remove the last stroke from the stroke array
|
|
wisphrc->ulCurrentStrokeCount--;
|
|
return hr;
|
|
}
|
|
|
|
temp = vrc->pLattice->nRealStrokes;
|
|
InterlockedExchange(&(wisphrc->uAbort), temp);
|
|
}
|
|
|
|
ptArray = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT WINAPI GetBestResultString(HRECOCONTEXT hrc, ULONG *pcwSize, WCHAR* pszBestResult)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
HRESULT hr = S_OK;
|
|
VRC *vrc = NULL;
|
|
ULONG i = 0;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadWritePtr(pcwSize, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// check the string pointer if needed
|
|
if ( pszBestResult &&
|
|
IsBadWritePtr (pszBestResult, (*pcwSize) * sizeof (*pszBestResult))
|
|
)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
vrc = (VRC*)wisphrc->hrc;
|
|
if (!vrc)
|
|
{
|
|
*pcwSize = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
if (!vrc->pLatticePath)
|
|
{
|
|
*pcwSize = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
if (!pszBestResult)
|
|
{
|
|
*pcwSize = vrc->pLatticePath->nChars;
|
|
return S_OK;
|
|
}
|
|
|
|
// Make the length realistic
|
|
if (*pcwSize > (ULONG)vrc->pLatticePath->nChars)
|
|
{
|
|
*pcwSize = vrc->pLatticePath->nChars;
|
|
}
|
|
|
|
// Is the buffer too small?
|
|
if (*pcwSize < (ULONG)vrc->pLatticePath->nChars)
|
|
{
|
|
hr = TPC_S_TRUNCATED;
|
|
}
|
|
|
|
for (i = 0; i < *pcwSize; i++)
|
|
{
|
|
pszBestResult[i] = vrc->pLatticePath->pElem[i].wChar;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// GetBestAlternate
|
|
//
|
|
// This function create the best alternate from the best segmentation
|
|
//
|
|
// Parameters:
|
|
// hrc [in] : the reco context
|
|
// pHrcAlt [out] : pointer to the handle of the alternate
|
|
/////////////////
|
|
HRESULT WINAPI GetBestAlternate(HRECOCONTEXT hrc, HRECOALT* pHrcAlt)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
HRESULT hr = S_OK;
|
|
ULONG cbSize = 0;
|
|
struct WispAlternate *pWispAlt = NULL;
|
|
VRC *vrc = NULL;
|
|
ULONG i = 0;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// First get the number of characters in the string
|
|
vrc = (VRC*)wisphrc->hrc;
|
|
if (!vrc)
|
|
{
|
|
return TPC_E_NOT_RELEVANT;
|
|
}
|
|
|
|
if (!vrc->pLatticePath)
|
|
{
|
|
// There is no ink
|
|
return TPC_E_NOT_RELEVANT;
|
|
}
|
|
|
|
cbSize = vrc->pLatticePath->nChars;
|
|
|
|
// Create the alternate
|
|
pWispAlt = (struct WispAlternate*)ExternAlloc(sizeof(struct WispAlternate));
|
|
if (!pWispAlt)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ZeroMemory(pWispAlt, sizeof(struct WispAlternate));
|
|
pWispAlt->iNumberOfColumns = cbSize;
|
|
pWispAlt->iLength = cbSize;
|
|
pWispAlt->OriginalRecoRange.iwcBegin = 0;
|
|
pWispAlt->OriginalRecoRange.cCount = cbSize;
|
|
pWispAlt->hrc = hrc;
|
|
if (cbSize)
|
|
{
|
|
pWispAlt->pColumnIndex = ExternAlloc(sizeof(ULONG)*cbSize);
|
|
if (!pWispAlt->pColumnIndex)
|
|
{
|
|
ExternFree(pWispAlt);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Initialize the column index array
|
|
for (i = 0; i<cbSize; i++)
|
|
{
|
|
pWispAlt->pColumnIndex[i] = vrc->pLatticePath->pElem[i].iStroke;
|
|
}
|
|
pWispAlt->pIndexInColumn = ExternAlloc(sizeof(ULONG)*cbSize);
|
|
if (!pWispAlt->pIndexInColumn)
|
|
{
|
|
ExternFree(pWispAlt->pColumnIndex);
|
|
ExternFree(pWispAlt);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
// The best alternate doe not always have the index 0 in the alternate column
|
|
// Initialize the index in column array
|
|
for (i = 0; i<cbSize; i++)
|
|
{
|
|
pWispAlt->pIndexInColumn[i] = vrc->pLatticePath->pElem[i].iAlt;
|
|
}
|
|
}
|
|
|
|
// create a tpg handle
|
|
*pHrcAlt = (HRECOALT)CreateTpgHandle(TPG_HRECOALT, pWispAlt);
|
|
if (0 == *pHrcAlt)
|
|
{
|
|
ExternFree (pWispAlt->pIndexInColumn);
|
|
ExternFree (pWispAlt->pColumnIndex);
|
|
ExternFree (pWispAlt);
|
|
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// internal implementation: destroy the wispalternate structure
|
|
HRESULT DestroyAlternateInternal(struct WispAlternate *wisphrcalt)
|
|
{
|
|
ExternFree(wisphrcalt->pColumnIndex);
|
|
ExternFree(wisphrcalt->pIndexInColumn);
|
|
ExternFree(wisphrcalt);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// DestroyAlternate
|
|
//
|
|
// This function destroys an alternate, freeing the allocated memory
|
|
//
|
|
// Parameters:
|
|
// hrcalt [in] : handle of the alternate to be destroyed
|
|
/////////////////
|
|
HRESULT WINAPI DestroyAlternate(HRECOALT hrcalt)
|
|
{
|
|
struct WispAlternate *wisphrcalt;
|
|
|
|
wisphrcalt = (struct WispAlternate *) DestroyTpgHandle (hrcalt, TPG_HRECOALT);
|
|
if (NULL == wisphrcalt)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
return DestroyAlternateInternal (wisphrcalt);
|
|
}
|
|
|
|
HRESULT WINAPI SetGuide(HRECOCONTEXT hrc, const RECO_GUIDE* pGuide, ULONG iIndex)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
HWXGUIDE hwxGuide;
|
|
HRESULT hr = S_OK;
|
|
BOOL bGuideAlreadySet = FALSE;
|
|
RECO_GUIDE rgOldGuide;
|
|
ULONG uiOldIndex = 0;
|
|
BOOL bIsOldGuideBox = FALSE;
|
|
BOOL bIsHRCAlreadyCreated = FALSE;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (pGuide != NULL && IsBadReadPtr(pGuide, sizeof(RECO_GUIDE)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (pGuide != NULL)
|
|
{
|
|
if ((pGuide->cHorzBox < 0 || pGuide->cVertBox < 0) || // invalid
|
|
(pGuide->cHorzBox == 0 && pGuide->cVertBox > 0) || // horizontal lined mode
|
|
(pGuide->cHorzBox > 0 && pGuide->cVertBox == 0) || // vertical lined mode
|
|
(g_pProbTable == NULL && pGuide->cHorzBox == 0 && pGuide->cVertBox == 0)) // free mode not allowed sometimes
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
if (pGuide == NULL && g_pProbTable == NULL)
|
|
{
|
|
// Can't do free mode, but got a NULL guide, so return an error.
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Is there already an HRC
|
|
if (wisphrc->hrc)
|
|
{
|
|
bIsHRCAlreadyCreated = TRUE;
|
|
}
|
|
|
|
// Save the old values in case the call to the
|
|
// recognizer fails
|
|
if (wisphrc->pGuide)
|
|
{
|
|
bGuideAlreadySet = TRUE;
|
|
rgOldGuide = *(wisphrc->pGuide);
|
|
uiOldIndex = wisphrc->uiGuideIndex;
|
|
bIsOldGuideBox = wisphrc->bIsBoxed;
|
|
}
|
|
|
|
// If there was no guide already present, allocate one
|
|
if (!wisphrc->pGuide)
|
|
wisphrc->pGuide = ExternAlloc(sizeof(RECO_GUIDE));
|
|
if (!wisphrc->pGuide)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// If the guide is NULL, then treat it as all zeros (free mode)
|
|
if (pGuide != NULL)
|
|
{
|
|
*(wisphrc->pGuide) = *pGuide;
|
|
}
|
|
else
|
|
{
|
|
ZeroMemory(wisphrc->pGuide, sizeof(RECO_GUIDE));
|
|
}
|
|
wisphrc->uiGuideIndex = iIndex;
|
|
|
|
// Check if we are in box mode or free input mode
|
|
if (wisphrc->pGuide->cHorzBox && wisphrc->pGuide->cVertBox)
|
|
{
|
|
// We are in the box api mode
|
|
// We need to have a proper conversion
|
|
ZeroMemory(&hwxGuide, sizeof(HWXGUIDE));
|
|
|
|
hwxGuide.cHorzBox = wisphrc->pGuide->cHorzBox;
|
|
hwxGuide.cVertBox = wisphrc->pGuide->cVertBox;
|
|
hwxGuide.cxBox = wisphrc->pGuide->cxBox;
|
|
hwxGuide.cyBox = wisphrc->pGuide->cyBox;
|
|
hwxGuide.xOrigin = wisphrc->pGuide->xOrigin;
|
|
hwxGuide.yOrigin = wisphrc->pGuide->yOrigin;
|
|
|
|
hwxGuide.cxOffset = wisphrc->pGuide->cxBase ;
|
|
hwxGuide.cyOffset = 0;
|
|
hwxGuide.cxWriting = wisphrc->pGuide->cxBox - (2 * wisphrc->pGuide->cxBase) ;
|
|
|
|
if (wisphrc->pGuide->cyBase > 0) {
|
|
hwxGuide.cyWriting = wisphrc->pGuide->cyBase ;
|
|
} else {
|
|
hwxGuide.cyWriting = wisphrc->pGuide->cyBox ;
|
|
}
|
|
|
|
hwxGuide.cyMid = 0 ;
|
|
hwxGuide.cyBase = 0 ;
|
|
hwxGuide.nDir = HWX_HORIZONTAL ;
|
|
|
|
// Is the hrc already created
|
|
if (bIsHRCAlreadyCreated)
|
|
{
|
|
// Are we already in box mode?
|
|
if (!wisphrc->bIsBoxed)
|
|
{
|
|
// We need to switch to a box hrc if possible
|
|
if (wisphrc->ulCurrentStrokeCount == 0)
|
|
{
|
|
// Destroy the previous context
|
|
DestroyHRC(wisphrc->hrc);
|
|
wisphrc->hrc = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
wisphrc->bIsBoxed = TRUE;
|
|
if (SUCCEEDED(hr) && !wisphrc->hrc)
|
|
{
|
|
hr = CreateHRCinContext(wisphrc);
|
|
if (FAILED(hr))
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (HwxSetGuide(wisphrc->hrc, &hwxGuide))
|
|
{
|
|
if (TRUE == HwxSetAbort(wisphrc->hrc, &(wisphrc->uAbort)))
|
|
{
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wisphrc->bIsBoxed = FALSE;
|
|
if (!wisphrc->hrc)
|
|
{
|
|
// We need to switch to a free hrc if possible
|
|
if (wisphrc->ulCurrentStrokeCount == 0)
|
|
{
|
|
// Destroy the previous context
|
|
HwxDestroy(wisphrc->hrc);
|
|
wisphrc->hrc = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
hr = CreateHRCinContext(wisphrc);
|
|
if (FAILED(hr))
|
|
hr = E_FAIL;
|
|
|
|
// we are in the free api mode
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (HRCR_OK == SetGuideHRC(wisphrc->hrc, (GUIDE *)wisphrc->pGuide, iIndex))
|
|
return S_OK;
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
// The calls did not succeed.
|
|
// If we allocated an hrc, destroy it
|
|
if (!bIsHRCAlreadyCreated && wisphrc->hrc)
|
|
{
|
|
if (wisphrc->bIsBoxed)
|
|
{
|
|
HwxDestroy(wisphrc->hrc);
|
|
}
|
|
else
|
|
{
|
|
DestroyHRC(wisphrc->hrc);
|
|
}
|
|
wisphrc->hrc = NULL;
|
|
}
|
|
// Set back the old guide
|
|
if (bGuideAlreadySet)
|
|
{
|
|
*(wisphrc->pGuide) = rgOldGuide;
|
|
wisphrc->bIsBoxed = bIsOldGuideBox;
|
|
wisphrc->uiGuideIndex = uiOldIndex;
|
|
}
|
|
else
|
|
{
|
|
ExternFree(wisphrc->pGuide);
|
|
wisphrc->pGuide = NULL;
|
|
wisphrc->bIsBoxed = FALSE;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT WINAPI GetGuide(HRECOCONTEXT hrc, RECO_GUIDE* pGuide, ULONG *piIndex)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadWritePtr(pGuide, sizeof(RECO_GUIDE)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsBadWritePtr(piIndex, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (!wisphrc->pGuide)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (wisphrc->pGuide)
|
|
{
|
|
*pGuide = *(wisphrc->pGuide);
|
|
}
|
|
|
|
if (piIndex)
|
|
{
|
|
*piIndex = wisphrc->uiGuideIndex;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT WINAPI AdviseInkChange(HRECOCONTEXT hrc, BOOL bNewStroke)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
InterlockedIncrement(&(wisphrc->uAbort));
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT WINAPI SetCACMode(HRECOCONTEXT hrc, int iMode)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
struct WispContext *wisphrc;
|
|
VRC *vrc;
|
|
HRECOCONTEXT CloneHrc = NULL;
|
|
HRC OldHrc = NULL;
|
|
int i = 0, j = 0;
|
|
int iCACMode = 0;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!wisphrc->bIsBoxed)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (iMode != CAC_FULL && iMode != CAC_PREFIX && iMode != CAC_RANDOM)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
vrc = (VRC*)wisphrc->hrc;
|
|
|
|
OldHrc = wisphrc->hrc;
|
|
|
|
wisphrc->hrc = NULL;
|
|
|
|
// Create the new context
|
|
wisphrc->hrc = HwxCreate(OldHrc);
|
|
|
|
if (!wisphrc->hrc)
|
|
{
|
|
wisphrc->hrc = OldHrc;
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (FALSE == HwxSetAbort(wisphrc->hrc, &(wisphrc->uAbort)))
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
|
|
// Set the CAC mode
|
|
if (iMode == CAC_FULL)
|
|
{
|
|
iCACMode = HWX_PARTIAL_ALL;
|
|
}
|
|
else
|
|
if (iMode == CAC_PREFIX)
|
|
{
|
|
iCACMode = HWX_PARTIAL_ORDER;
|
|
}
|
|
else
|
|
if (iMode == CAC_RANDOM)
|
|
{
|
|
iCACMode = HWX_PARTIAL_FREE;
|
|
}
|
|
|
|
if (!HwxSetPartial(wisphrc->hrc, iCACMode))
|
|
{
|
|
// Put things back together
|
|
HwxDestroy(wisphrc->hrc);
|
|
wisphrc->hrc = ((struct WispContext*)OldHrc)->hrc;
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
wisphrc->bIsCAC = TRUE;
|
|
wisphrc->iCACMode = iMode;
|
|
wisphrc->bCACEndInk = FALSE;
|
|
|
|
// TO DO
|
|
// TO DO
|
|
//
|
|
// We probably need to store the original ink, not use the smoothed and merged ink that
|
|
// is store in the Lattice...
|
|
|
|
// We need to get the ink from the old context, if there was an old context
|
|
if (vrc != NULL)
|
|
{
|
|
for (i = 0; i < vrc->pLattice->nStrokes; i++)
|
|
{
|
|
// We need to reorder the strokes to have them in the same order
|
|
for (j = 0; j < vrc->pLattice->nStrokes; j++)
|
|
{
|
|
if (vrc->pLattice->pStroke[j].iOrder == i)
|
|
{
|
|
// Add the Stroke to the new context
|
|
if (!HwxInput(wisphrc->hrc, vrc->pLattice->pStroke[j].pts, vrc->pLattice->pStroke[j].nInk, vrc->pLattice->pStroke[j].timeStart))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
wisphrc->uAbort = vrc->pLattice->nStrokes;
|
|
if (vrc->fBoxedInput)
|
|
HwxDestroy(OldHrc);
|
|
else
|
|
DestroyHRC(OldHrc);
|
|
}
|
|
else
|
|
{
|
|
wisphrc->uAbort = 0;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT WINAPI EndInkInput(HRECOCONTEXT hrc)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (wisphrc->bIsBoxed)
|
|
{
|
|
if (HwxEndInput(wisphrc->hrc))
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
if (HRCR_OK == EndPenInputHRC(wisphrc->hrc))
|
|
return S_OK;
|
|
}
|
|
|
|
if (!wisphrc->ulCurrentStrokeCount)
|
|
{
|
|
return S_OK; // We do not have ink yet
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Given a recognition context, create a new one which has no ink in it, but
|
|
// is otherwise identical. An error is returned if there are any allocation
|
|
// problems (which should be the only types of errors).
|
|
HRESULT WINAPI CloneContext(HRECOCONTEXT hrc, HRECOCONTEXT* pCloneHrc)
|
|
{
|
|
struct WispContext *pWispContext = NULL;
|
|
struct WispContext *wisphrc;
|
|
HRESULT hRes = S_OK, hr = S_OK;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadWritePtr(pCloneHrc, sizeof(HRECOCONTEXT)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
pWispContext = (struct WispContext*)ExternAlloc(sizeof(struct WispContext));
|
|
if (!pWispContext)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Did we already create a context???
|
|
if (!wisphrc->hrc)
|
|
{
|
|
// The context was not already created
|
|
memcpy(pWispContext, wisphrc, sizeof(struct WispContext));
|
|
// If a guide was created, then we would have a context
|
|
ASSERT(!wisphrc->pGuide);
|
|
pWispContext->pGuide = NULL;
|
|
// You can't get a lattice until after processing has been done (in a context)
|
|
ASSERT(!wisphrc->pLattice);
|
|
pWispContext->pLattice = NULL;
|
|
pWispContext->pLatticeProperties = NULL;
|
|
pWispContext->pLatticePropertyValues = NULL;
|
|
pWispContext->ppLatticeProperties = NULL;
|
|
|
|
// Copy the text context
|
|
if (wisphrc->bHasTextContext)
|
|
{
|
|
pWispContext->bHasTextContext = TRUE;
|
|
pWispContext->wszBefore = Externwcsdup(wisphrc->wszBefore);
|
|
pWispContext->wszAfter = Externwcsdup(wisphrc->wszAfter);
|
|
if (pWispContext->wszBefore == NULL || pWispContext->wszAfter == NULL)
|
|
{
|
|
ExternFree(pWispContext->wszBefore);
|
|
ExternFree(pWispContext->wszAfter);
|
|
ExternFree(pWispContext);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pWispContext->bHasTextContext = FALSE;
|
|
pWispContext->wszBefore = NULL;
|
|
pWispContext->wszAfter = NULL;
|
|
}
|
|
|
|
// Copy factoid setting
|
|
if (wisphrc->wszFactoid)
|
|
{
|
|
pWispContext->wszFactoid = Externwcsdup(wisphrc->wszFactoid);
|
|
if (pWispContext->wszFactoid == NULL)
|
|
{
|
|
ExternFree(pWispContext->wszAfter);
|
|
ExternFree(pWispContext->wszBefore);
|
|
ExternFree(pWispContext);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pWispContext->wszFactoid = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Depending of whether we are in box mode create the hwx hrc
|
|
if (!wisphrc->bIsBoxed)
|
|
{
|
|
pWispContext->bIsBoxed = FALSE;
|
|
pWispContext->hrc = CreateCompatibleHRC(wisphrc->hrc, NULL);
|
|
}
|
|
else
|
|
{
|
|
pWispContext->bIsBoxed = TRUE;
|
|
pWispContext->hrc = HwxCreate(wisphrc->hrc);
|
|
}
|
|
if (!pWispContext->hrc)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Set the context variables
|
|
if (SUCCEEDED(hr) && wisphrc->bHasTextContext)
|
|
{
|
|
pWispContext->bHasTextContext = TRUE;
|
|
pWispContext->wszBefore = Externwcsdup(wisphrc->wszBefore);
|
|
pWispContext->wszAfter = Externwcsdup(wisphrc->wszAfter);
|
|
if (pWispContext->wszBefore == NULL || pWispContext->wszAfter == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pWispContext->bHasTextContext = FALSE;
|
|
pWispContext->wszBefore = NULL;
|
|
pWispContext->wszAfter = NULL;
|
|
}
|
|
|
|
// Copy flags
|
|
pWispContext->dwFlags = wisphrc->dwFlags;
|
|
|
|
// Copy factoid setting
|
|
if (SUCCEEDED(hr) && wisphrc->wszFactoid)
|
|
{
|
|
pWispContext->wszFactoid = Externwcsdup(wisphrc->wszFactoid);
|
|
if (pWispContext->wszFactoid == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pWispContext->wszFactoid = NULL;
|
|
}
|
|
|
|
// Set the guide for the Wisp structure
|
|
if (SUCCEEDED(hr) && wisphrc->pGuide)
|
|
{
|
|
pWispContext->pGuide = ExternAlloc(sizeof(RECO_GUIDE));
|
|
if (!pWispContext->pGuide)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
*(pWispContext->pGuide) = *(wisphrc->pGuide);
|
|
pWispContext->uiGuideIndex = wisphrc->uiGuideIndex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pWispContext->pGuide = NULL;
|
|
}
|
|
|
|
// Set the abort for hwx
|
|
pWispContext->uAbort = 0;
|
|
if (SUCCEEDED(hr) && pWispContext->bIsBoxed)
|
|
{
|
|
if (!HwxSetAbort(pWispContext->hrc, &(pWispContext->uAbort)))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
pWispContext->ulCurrentStrokeCount = 0;
|
|
pWispContext->bCACEndInk = FALSE;
|
|
pWispContext->bIsCAC = FALSE;
|
|
|
|
// Set the CAC Mode
|
|
if (SUCCEEDED(hr) && wisphrc->bIsCAC)
|
|
{
|
|
pWispContext->bIsCAC = TRUE;
|
|
pWispContext->iCACMode = wisphrc->iCACMode;
|
|
}
|
|
}
|
|
|
|
// Clean the lattice
|
|
pWispContext->pLattice = NULL;
|
|
pWispContext->pLatticeProperties = NULL;
|
|
pWispContext->pLatticePropertyValues = NULL;
|
|
pWispContext->ppLatticeProperties = NULL;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// create a tpg handle
|
|
*pCloneHrc = (HRECOCONTEXT)CreateTpgHandle(TPG_HRECOCONTEXT, pWispContext);
|
|
if (NULL == (*pCloneHrc))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
hRes = DestroyContextInternal(pWispContext);
|
|
ASSERT(SUCCEEDED(hRes));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// ResetContext
|
|
// This function keeps the settings on the passed reco context
|
|
// but purges it of the ink it contains. If the EndInkInput
|
|
// had been called on the reco context, Reset context will
|
|
// now allow more ink to be entered.
|
|
//
|
|
// Parameter:
|
|
// hrc [in] : the handle to the reco context
|
|
/////////////////////////////////////////////////////////
|
|
HRESULT WINAPI ResetContext(HRECOCONTEXT hrc)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
HRESULT hr = S_OK;
|
|
HRC hrcold = NULL;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!wisphrc->hrc)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// Save the old HRC (in case of an error), and put in the new one
|
|
hrcold = wisphrc->hrc;
|
|
|
|
if (!wisphrc->bIsBoxed)
|
|
{
|
|
wisphrc->hrc = CreateCompatibleHRC(wisphrc->hrc, NULL);
|
|
}
|
|
else
|
|
{
|
|
wisphrc->hrc = HwxCreate(wisphrc->hrc);
|
|
}
|
|
|
|
if (!wisphrc->hrc)
|
|
{
|
|
wisphrc->hrc = hrcold;
|
|
return E_FAIL;
|
|
}
|
|
|
|
// If there was a guide, put it back
|
|
if (SUCCEEDED(hr) && wisphrc->pGuide)
|
|
{
|
|
hr = SetGuide(hrc, wisphrc->pGuide, wisphrc->uiGuideIndex);
|
|
}
|
|
if (SUCCEEDED(hr) && wisphrc->bIsCAC)
|
|
{
|
|
hr = SetCACMode(hrc, wisphrc->iCACMode);
|
|
}
|
|
if (SUCCEEDED(hr) && wisphrc->bHasTextContext)
|
|
{
|
|
hr = SetTextContext(hrc,
|
|
wcslen(wisphrc->wszBefore), wisphrc->wszBefore,
|
|
wcslen(wisphrc->wszAfter), wisphrc->wszAfter);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SetFlags(hrc, wisphrc->dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (wisphrc->wszFactoid)
|
|
hr = SetFactoid(hrc, wcslen(wisphrc->wszFactoid), wisphrc->wszFactoid);
|
|
else
|
|
hr = SetFactoid(hrc, 0, wisphrc->wszFactoid);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
// Something went wrong. Restore the context to its original state.
|
|
if (!wisphrc->bIsBoxed)
|
|
{
|
|
DestroyHRC(wisphrc->hrc);
|
|
}
|
|
else
|
|
{
|
|
HwxDestroy(wisphrc->hrc);
|
|
}
|
|
wisphrc->hrc = hrcold;
|
|
return hr;
|
|
}
|
|
|
|
// These changes are done last, because they can't be undone easily.
|
|
// All error cases have already been taken care of above.
|
|
wisphrc->ulCurrentStrokeCount = 0;
|
|
wisphrc->uAbort = 0;
|
|
wisphrc->bCACEndInk = FALSE;
|
|
|
|
if (wisphrc->pLattice)
|
|
{
|
|
hr = FreeRecoLattice(wisphrc);
|
|
}
|
|
wisphrc->pLattice = NULL;
|
|
|
|
if (!wisphrc->bIsBoxed)
|
|
{
|
|
DestroyHRC(hrcold);
|
|
}
|
|
else
|
|
{
|
|
HwxDestroy(hrcold);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Sets the prefix and suffix context for the recognition context. Can
|
|
// return errors on memory allocation failure. Note that this function
|
|
// is called with the strings pointing at the strings already in the HRC,
|
|
// so we need to be careful not to free the old strings before copying them.
|
|
HRESULT WINAPI SetTextContext(HRECOCONTEXT hrc, ULONG cwcBefore, const WCHAR *pwcBefore, ULONG cwcAfter, const WCHAR *pwcAfter)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
struct WispContext *wisphrc;
|
|
WCHAR *wszBefore, *wszAfter;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if ( IsBadReadPtr(pwcBefore, cwcBefore * sizeof(WCHAR)) ||
|
|
IsBadReadPtr(pwcAfter, cwcAfter * sizeof(WCHAR))
|
|
)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
wszBefore = ExternAlloc((cwcBefore + 1) * sizeof(WCHAR));
|
|
if (wszBefore == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
wszAfter = ExternAlloc((cwcAfter + 1) * sizeof(WCHAR));
|
|
if (wszAfter == NULL)
|
|
{
|
|
ExternFree(wszBefore);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
memcpy(wszBefore, pwcBefore, cwcBefore * sizeof(WCHAR));
|
|
wszBefore[cwcBefore] = 0;
|
|
memcpy(wszAfter, pwcAfter, cwcAfter * sizeof(WCHAR));
|
|
wszAfter[cwcAfter] = 0;
|
|
|
|
// If we have a context already, then try to set this context.
|
|
// The only errors are memory allocation errors, so we don't need
|
|
// to create a context here to make sure the call will succeed.
|
|
if (wisphrc->hrc)
|
|
{
|
|
if (!SetHwxCorrectionContext(wisphrc->hrc, wszBefore, wszAfter))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
// If everything went okay, then we can update the context.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
wisphrc->bHasTextContext = TRUE;
|
|
ExternFree(wisphrc->wszBefore);
|
|
ExternFree(wisphrc->wszAfter);
|
|
wisphrc->wszBefore = wszBefore;
|
|
wisphrc->wszAfter = wszAfter;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Create an alternate (returned by setting the pointer ppAlt). The contents of
|
|
// the alternate are determined by pAltStruct and pdbList.
|
|
HRESULT CreateDifBreakAlternate(DifBreakList* pdbList, DifBreakAltStruct *pAltStruct, struct WispAlternate **ppAlt)
|
|
{
|
|
int i = 0;
|
|
VRC *vrc = pAltStruct->vrc;
|
|
DiffBreakElement *pCur = NULL;
|
|
|
|
*ppAlt = (struct WispAlternate*)ExternAlloc(sizeof(struct WispAlternate));
|
|
if (!*ppAlt)
|
|
return E_OUTOFMEMORY;
|
|
ZeroMemory(*ppAlt, sizeof(struct WispAlternate));
|
|
// Initialize the new alternate structure
|
|
(*ppAlt)->iLength = (*ppAlt)->iNumberOfColumns = pdbList->iColumnCount;
|
|
(*ppAlt)->pColumnIndex = (int*)ExternAlloc(sizeof(int)*(*ppAlt)->iNumberOfColumns);
|
|
if (!(*ppAlt)->pColumnIndex)
|
|
{
|
|
ExternFree(*ppAlt);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
(*ppAlt)->pIndexInColumn = (int*)ExternAlloc(sizeof(int)*(*ppAlt)->iNumberOfColumns);
|
|
if (!(*ppAlt)->pIndexInColumn)
|
|
{
|
|
ExternFree((*ppAlt)->pColumnIndex);
|
|
ExternFree(*ppAlt);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
(*ppAlt)->OriginalRecoRange.iwcBegin = pAltStruct->iFirstChar;
|
|
(*ppAlt)->OriginalRecoRange.cCount = pAltStruct->iLastChar -
|
|
pAltStruct->iFirstChar + 1;
|
|
pCur = pdbList->pFirst;
|
|
for (i = 0; i < pdbList->iColumnCount; i++)
|
|
{
|
|
// Add the ColumnIndex
|
|
(*ppAlt)->pColumnIndex[i] = pCur->iColumn;
|
|
(*ppAlt)->pIndexInColumn[i] = pCur->iIndex;
|
|
pCur = pCur->pNext;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL AddToDefSegList(DifBreakList* pdbList, DifBreakAltStruct *pAltStruct)
|
|
{
|
|
AltRank *pAltRank = NULL;
|
|
HRESULT hr = S_OK, hRes = S_OK;
|
|
AltRankList *pAltRankList = pAltStruct->paltRankList;
|
|
AltRank *pPrev = NULL, *pCur;
|
|
UINT i = 0;
|
|
DiffBreakElement *pCurrent = NULL;
|
|
int j = 0;
|
|
|
|
// Doesn't make sense to add alternates to a list when no alternates are allowed.
|
|
ASSERT(pAltStruct->ulMax > 0);
|
|
if (pAltStruct->ulMax == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Is the score even interesting?
|
|
if (pAltRankList->ulSize == pAltStruct->ulMax &&
|
|
!pdbList->bCurrentPath &&
|
|
pAltRankList->pLast != NULL &&
|
|
pAltRankList->pLast->fScore >= pdbList->score)
|
|
return TRUE;
|
|
// Is the decomposition interesting (depending on the recognition mode)
|
|
// For now yes, everyting is interesting!
|
|
// Create the alternate
|
|
pAltRank = (AltRank*)ExternAlloc(sizeof(AltRank));
|
|
if (!pAltRank)
|
|
return FALSE;
|
|
|
|
pAltRank->fScore = pdbList->score;
|
|
// All paths created here are not on the current path
|
|
pAltRank->bCurrentPath = pdbList->bCurrentPath;
|
|
pAltRank->next = NULL;
|
|
// Add the new alternate at the current location
|
|
hr = CreateDifBreakAlternate(pdbList, pAltStruct, &(pAltRank->wispalt));
|
|
if (FAILED(hr))
|
|
{
|
|
ExternFree(pAltRank);
|
|
return FALSE;
|
|
}
|
|
if (!pAltRankList->pFirst)
|
|
{
|
|
// Add at the start of the location
|
|
pAltRankList->pFirst = pAltRank;
|
|
pAltRankList->pLast = pAltRank;
|
|
pAltRankList->ulSize = 1;
|
|
return TRUE;
|
|
}
|
|
// If the new alternate is the current path, then it goes to the top of the list.
|
|
// If not, and the current top of list is not on the current path, then compare scores.
|
|
if (pdbList->bCurrentPath ||
|
|
(!pAltRankList->pFirst->bCurrentPath && pAltRankList->pFirst->fScore < pdbList->score))
|
|
{
|
|
// Add at the start of the location
|
|
pAltRank->next = pAltRankList->pFirst;
|
|
pAltRankList->pFirst = pAltRank;
|
|
if (pAltRankList->ulSize == pAltStruct->ulMax)
|
|
{
|
|
// Delete the last element
|
|
hRes = DestroyAlternateInternal(pAltRankList->pLast->wispalt);
|
|
ASSERT(SUCCEEDED(hRes));
|
|
ExternFree(pAltRankList->pLast);
|
|
// Get a pointer to the last element
|
|
pCur = pAltRankList->pFirst;
|
|
while(pCur->next != pAltRankList->pLast)
|
|
pCur = pCur->next;
|
|
pAltRankList->pLast = pCur;
|
|
pCur->next = NULL;
|
|
}
|
|
else
|
|
{
|
|
pAltRankList->ulSize++;
|
|
}
|
|
return TRUE;
|
|
}
|
|
pCur = pAltRankList->pFirst;
|
|
// Insert the link at the right location
|
|
for (i = 0; i < pAltRankList->ulSize - 1; i++)
|
|
{
|
|
pPrev = pCur;
|
|
pCur = pCur->next;
|
|
if (pCur->fScore < pdbList->score)
|
|
{
|
|
// insert at the pPrev
|
|
pAltRank->next = pCur;
|
|
pPrev->next = pAltRank;
|
|
if (pAltRankList->ulSize == pAltStruct->ulMax)
|
|
{
|
|
// Delete the last element
|
|
HRESULT hrDA = DestroyAlternateInternal(pAltRankList->pLast->wispalt);
|
|
ASSERT(SUCCEEDED(hrDA));
|
|
ExternFree(pAltRankList->pLast);
|
|
// Get a pointer to the last element
|
|
pCur = pAltRankList->pFirst;
|
|
while(pCur->next != pAltRankList->pLast)
|
|
pCur = pCur->next;
|
|
pAltRankList->pLast = pCur;
|
|
pCur->next = NULL;
|
|
}
|
|
else
|
|
{
|
|
pAltRankList->ulSize++;
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
// We are actually adding at the end of the list
|
|
pAltRank->next = NULL;
|
|
pAltRankList->pLast->next = pAltRank;
|
|
pAltRankList->pLast = pAltRank;
|
|
pAltRankList->ulSize++; // obviously we still have room
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
static float GetScore(LATTICE *pLattice, int iStroke, int iAlt)
|
|
{
|
|
if (pLattice->fUseGuide)
|
|
{
|
|
return pLattice->pAltList[iStroke].alts[iAlt].logProb;
|
|
}
|
|
else
|
|
{
|
|
return pLattice->pAltList[iStroke].alts[iAlt].logProb * pLattice->pAltList[iStroke].alts[iAlt].nStrokes;
|
|
}
|
|
}
|
|
*/
|
|
|
|
BOOL GetRecDifSegAltList(int iCurrentStroke, int iCurrentIndex, DifBreakList* pdbList, DifBreakAltStruct *pAltStruct)
|
|
{
|
|
int iNextStroke = 0;
|
|
int i = 0, j = 0, l = 0;
|
|
DiffBreakElement dbElement;
|
|
DiffBreakElement dbSpaceElement;
|
|
float fOriginalScore = pdbList->score;
|
|
BOOL bOriginalCurrentPath = pdbList->bCurrentPath;
|
|
BOOL bSomethingAdded = FALSE;
|
|
BOOL bMainSeg = FALSE;
|
|
int iNumberOfStrokes = 0;
|
|
BOOL bAllBreakSkip = FALSE;
|
|
|
|
int iNextNextStroke = 0;
|
|
BOOL bSameBreak = FALSE;
|
|
BOOL bOkay = TRUE;
|
|
|
|
iNextStroke = iCurrentStroke -
|
|
pAltStruct->vrc->pLattice->pAltList[iCurrentStroke].alts[iCurrentIndex].nStrokes;
|
|
// Check if we are at the end (we include uiFirstIndex)
|
|
if (pAltStruct->iMode == LARGE_BREAKS)
|
|
{
|
|
if (pAltStruct->vrc->pLattice->pAltList[iCurrentStroke].alts[iCurrentIndex].nStrokes
|
|
> iCurrentStroke-pAltStruct->iFirstStroke)
|
|
{
|
|
// Yes this is a last (or rather "first") stroke
|
|
// But does this stroke correspond to the start of a stroke from the main
|
|
// segmentation?
|
|
bMainSeg = FALSE;
|
|
for (j = pAltStruct->iLastChar; j>=0; j--)
|
|
{
|
|
if (pAltStruct->vrc->pLatticePath->pElem[j].iStroke -
|
|
pAltStruct->vrc->pLatticePath->pElem[j].nStrokes ==
|
|
iNextStroke)
|
|
{
|
|
bMainSeg = TRUE;
|
|
pAltStruct->iFirstChar = j;
|
|
break;
|
|
}
|
|
}
|
|
// Add this to the stroke list
|
|
if (bMainSeg)
|
|
return AddToDefSegList(pdbList, pAltStruct);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pAltStruct->vrc->pLattice->pAltList[iCurrentStroke].alts[iCurrentIndex].nStrokes
|
|
== iCurrentStroke-pAltStruct->iFirstStroke+1)
|
|
{
|
|
// We are at the end
|
|
for (j = pAltStruct->iLastChar; j>=0; j--)
|
|
{
|
|
if (pAltStruct->vrc->pLatticePath->pElem[j].iStroke -
|
|
pAltStruct->vrc->pLatticePath->pElem[j].nStrokes ==
|
|
iNextStroke)
|
|
{
|
|
pAltStruct->iFirstChar = j;
|
|
return AddToDefSegList(pdbList, pAltStruct);
|
|
}
|
|
}
|
|
// This is an error case
|
|
return FALSE;
|
|
}
|
|
if (pAltStruct->vrc->pLattice->pAltList[iCurrentStroke].alts[iCurrentIndex].nStrokes
|
|
> iCurrentStroke-pAltStruct->iFirstStroke+1)
|
|
{
|
|
// Not an error, we just stepped back too far in the lattice.
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// We are not at the end (or start) of the alternate, dig deeper
|
|
|
|
// First, see if we need a space
|
|
if (pAltStruct->vrc->pLattice->pAltList[iNextStroke].fSpaceAfterStroke)
|
|
{
|
|
dbSpaceElement.iColumn = iNextStroke;
|
|
dbSpaceElement.iIndex = SPACE_ALT_ID;
|
|
dbSpaceElement.pNext = pdbList->pFirst;
|
|
pdbList->iColumnCount++;
|
|
pdbList->pFirst = &dbSpaceElement;
|
|
}
|
|
|
|
// Then add on the placeholder for the current character
|
|
dbElement.iColumn = iNextStroke;
|
|
dbElement.pNext = pdbList->pFirst;
|
|
pdbList->iColumnCount++;
|
|
pdbList->pFirst = &dbElement;
|
|
|
|
// In the case of ALT_BREAKS_SAME, get the number of strokes of the best result's column
|
|
if (pAltStruct->iMode == ALT_BREAKS_SAME)
|
|
{
|
|
iNumberOfStrokes = -1;
|
|
for (i = 0; i < pAltStruct->vrc->pLattice->pAltList[iNextStroke].nUsed; i++)
|
|
{
|
|
if (pAltStruct->vrc->pLattice->pAltList[iNextStroke].alts[i].fCurrentPath)
|
|
{
|
|
iNumberOfStrokes = pAltStruct->vrc->pLattice->pAltList[iNextStroke].alts[i].nStrokes;
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(iNumberOfStrokes>0);
|
|
// If we don't find the current path, something has gone wrong. The
|
|
// rest of the code will behave sensibly, though, so continue.
|
|
}
|
|
|
|
for (i = 0; i < pAltStruct->vrc->pLattice->pAltList[iNextStroke].nUsed; i++)
|
|
{
|
|
// TBD:
|
|
// Here we should have an optimization to know if
|
|
// we should continue processing the alternates with the
|
|
// same decomposition
|
|
if (pAltStruct->iMode == ALT_BREAKS_SAME)
|
|
if (pAltStruct->vrc->pLattice->pAltList[iNextStroke].alts[i].nStrokes != iNumberOfStrokes)
|
|
continue;
|
|
|
|
// If we have been on the current path so far and are still on it with this
|
|
// node, then we are staying on the current path
|
|
pdbList->bCurrentPath = bOriginalCurrentPath &&
|
|
pAltStruct->vrc->pLattice->pAltList[iNextStroke].alts[i].fCurrentPath;
|
|
|
|
// Only need to skip alternates if we are trying to get one of each
|
|
// segmentation. And we never need to skip the current path, since we
|
|
// always want to return that segmentation.
|
|
if (pAltStruct->iMode == ALT_BREAKS_UNIQUE && !pdbList->bCurrentPath)
|
|
{
|
|
bAllBreakSkip = FALSE;
|
|
// Did we already go over an alternate with the same number of strokes?
|
|
for (l = 0; l<i; l++)
|
|
{
|
|
if (pAltStruct->vrc->pLattice->pAltList[iNextStroke].alts[i].nStrokes ==
|
|
pAltStruct->vrc->pLattice->pAltList[iNextStroke].alts[l].nStrokes)
|
|
{
|
|
bAllBreakSkip = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (bAllBreakSkip)
|
|
continue;
|
|
// If we have been on the current path so far, but are now considering a
|
|
// character off the current path,
|
|
// then skip it if there is a currrent path character later in the alt
|
|
// list with the same number of strokes.
|
|
if (!pdbList->bCurrentPath && bOriginalCurrentPath)
|
|
{
|
|
for (l = i + 1; l < pAltStruct->vrc->pLattice->pAltList[iNextStroke].nUsed; l++)
|
|
{
|
|
if (pAltStruct->vrc->pLattice->pAltList[iNextStroke].alts[l].fCurrentPath &&
|
|
pAltStruct->vrc->pLattice->pAltList[iNextStroke].alts[i].nStrokes ==
|
|
pAltStruct->vrc->pLattice->pAltList[iNextStroke].alts[l].nStrokes)
|
|
{
|
|
bAllBreakSkip = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (bAllBreakSkip) continue;
|
|
}
|
|
dbElement.iIndex = i;
|
|
pdbList->score = fOriginalScore +
|
|
pAltStruct->vrc->pLattice->pAltList[iNextStroke].alts[i].logProb;
|
|
// GetScore(pAltStruct->vrc->pLattice, iNextStroke, i);
|
|
if (!GetRecDifSegAltList(iNextStroke, i, pdbList, pAltStruct))
|
|
bOkay = FALSE;
|
|
}
|
|
pdbList->iColumnCount--;
|
|
pdbList->pFirst = dbElement.pNext;
|
|
// Unwind the space if necessary
|
|
if (pAltStruct->vrc->pLattice->pAltList[iNextStroke].fSpaceAfterStroke)
|
|
{
|
|
pdbList->iColumnCount--;
|
|
pdbList->pFirst = dbSpaceElement.pNext;
|
|
}
|
|
pdbList->score = fOriginalScore;
|
|
pdbList->bCurrentPath = bOriginalCurrentPath;
|
|
return bOkay;
|
|
}
|
|
|
|
HRESULT GetDifSegAltList(VRC *vrc, int iLastStroke, int iFirstStroke, ULONG ulMax, AltRankList *pAltRankList, int iMode, int iFirstChar, int iLastChar)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int iStroke = 0;
|
|
int i = 0, j = 0, l = 0;
|
|
DifBreakList dbList;
|
|
DiffBreakElement dbElement;
|
|
BOOL bGoodStart = FALSE;
|
|
BOOL bAllBreakSkip = FALSE;
|
|
DifBreakAltStruct altStruct;
|
|
int iNumberOfStrokes = 0;
|
|
|
|
// Get the last Column alternates, they should contain uiLastStroke
|
|
// For each of these alternate, get the complete alternate list, stopping
|
|
// at uiFirstStroke.
|
|
// Initialize the data
|
|
altStruct.iFirstStroke = iFirstStroke;
|
|
altStruct.iFirstChar = iFirstChar;
|
|
altStruct.iLastChar = iLastChar;
|
|
altStruct.iMode = iMode;
|
|
altStruct.ulMax = ulMax;
|
|
altStruct.vrc = vrc;
|
|
altStruct.paltRankList = pAltRankList;
|
|
altStruct.paltRankList->pFirst = altStruct.paltRankList->pLast = NULL;
|
|
altStruct.paltRankList->ulSize = 0;
|
|
dbList.iColumnCount = 1;
|
|
dbList.pFirst = &dbElement;
|
|
dbList.score = 0.0;
|
|
// The starting last stroke should no be further away than 35 strokes from
|
|
// the uiLastStroke
|
|
if (iMode == LARGE_BREAKS)
|
|
iStroke = (vrc->pLattice->nStrokes > iLastStroke + 35 ? iLastStroke + 35 : vrc->pLattice->nStrokes - 1);
|
|
else
|
|
iStroke = iLastStroke;
|
|
while (iStroke >= iLastStroke)
|
|
{
|
|
// The stroke has to be one of the main decomposition
|
|
if (iMode == LARGE_BREAKS)
|
|
{
|
|
bGoodStart = FALSE;
|
|
for (i = 0; i < vrc->pLattice->pAltList[iStroke].nUsed; i++)
|
|
{
|
|
if (vrc->pLattice->pAltList[iStroke].alts[i].fCurrentPath)
|
|
{
|
|
bGoodStart = TRUE;
|
|
// Get the character number in the best alternate string
|
|
for (j = 0; j < vrc->pLatticePath->nChars; j++)
|
|
{
|
|
if (vrc->pLatticePath->pElem[j].iStroke == iStroke)
|
|
{
|
|
altStruct.iLastChar = j;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
altStruct.iLastChar = iLastChar;
|
|
bGoodStart = TRUE;
|
|
}
|
|
if (bGoodStart)
|
|
{
|
|
// In the case of ALT_BREAKS_SAME, get the number of strokes of the best result's column
|
|
if (iMode == ALT_BREAKS_SAME)
|
|
{
|
|
iNumberOfStrokes = -1;
|
|
for (i = 0; i < vrc->pLattice->pAltList[iStroke].nUsed; i++)
|
|
{
|
|
if (vrc->pLattice->pAltList[iStroke].alts[i].fCurrentPath)
|
|
{
|
|
iNumberOfStrokes = vrc->pLattice->pAltList[iStroke].alts[i].nStrokes;
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(iNumberOfStrokes>0);
|
|
if (iNumberOfStrokes <= 0)
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
|
|
// Search if there is an alternate that contains uiLastStroke
|
|
for (i = 0; i < vrc->pLattice->pAltList[iStroke].nUsed; i++)
|
|
{
|
|
if (iMode == ALT_BREAKS_SAME)
|
|
if (vrc->pLattice->pAltList[iStroke].alts[i].nStrokes != iNumberOfStrokes)
|
|
continue;
|
|
|
|
if (iMode == ALT_BREAKS_UNIQUE && !vrc->pLattice->pAltList[iStroke].alts[i].fCurrentPath)
|
|
{
|
|
// Did we already go over an alternate with the same number of strokes?
|
|
bAllBreakSkip = FALSE;
|
|
for (l = 0; l<i; l++)
|
|
{
|
|
if (vrc->pLattice->pAltList[iStroke].alts[i].nStrokes ==
|
|
vrc->pLattice->pAltList[iStroke].alts[l].nStrokes)
|
|
{
|
|
bAllBreakSkip = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (bAllBreakSkip) continue;
|
|
|
|
// If we are considering a character which is not on the current path,
|
|
// then skip it if there is a current path character later in the alt
|
|
// list with the same number of strokes.
|
|
if (!vrc->pLattice->pAltList[iStroke].alts[i].fCurrentPath)
|
|
{
|
|
for (l = i + 1; l < vrc->pLattice->pAltList[iStroke].nUsed; l++)
|
|
{
|
|
if (vrc->pLattice->pAltList[iStroke].alts[l].fCurrentPath &&
|
|
vrc->pLattice->pAltList[iStroke].alts[i].nStrokes ==
|
|
vrc->pLattice->pAltList[iStroke].alts[l].nStrokes)
|
|
{
|
|
bAllBreakSkip = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (bAllBreakSkip) continue;
|
|
}
|
|
if (vrc->pLattice->pAltList[iStroke].alts[i].nStrokes > iStroke-iLastStroke)
|
|
{
|
|
dbElement.iColumn = iStroke;
|
|
dbElement.iIndex = i;
|
|
dbElement.pNext = NULL;
|
|
dbList.bCurrentPath = vrc->pLattice->pAltList[iStroke].alts[i].fCurrentPath;
|
|
|
|
// We found one that contains uiLastStroke
|
|
if (!GetRecDifSegAltList(iStroke, i, &dbList, &altStruct))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
iStroke--;
|
|
}
|
|
|
|
// If something went wrong, then clean up before returning
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
AltRank *pCur = pAltRankList->pFirst;
|
|
while (pCur != NULL)
|
|
{
|
|
AltRank *pNext = pCur->next;
|
|
DestroyAlternateInternal(pCur->wispalt);
|
|
ExternFree(pCur);
|
|
pCur = pNext;
|
|
}
|
|
pAltRankList->pFirst = pAltRankList->pLast = NULL;
|
|
pAltRankList->ulSize = 0;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT FitAlternateToRecoRange(VRC *vrc, struct WispAlternate *wispalt, RECO_RANGE recoRange)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int *newColumns = NULL;
|
|
int *newIndexes = NULL;
|
|
int iCurrent = 0, j = 0;
|
|
UINT i = 0;
|
|
int iNumberOfColumns = 0;
|
|
|
|
// Return straight away if the reco range is already good
|
|
if (recoRange.iwcBegin == wispalt->OriginalRecoRange.iwcBegin &&
|
|
recoRange.cCount == wispalt->OriginalRecoRange.cCount)
|
|
return S_OK;
|
|
// Allocate the new arrays
|
|
iNumberOfColumns = wispalt->iNumberOfColumns + recoRange.cCount - wispalt->OriginalRecoRange.cCount;
|
|
newColumns = (int*)ExternAlloc(sizeof(int)*iNumberOfColumns);
|
|
if (!newColumns)
|
|
return E_OUTOFMEMORY;
|
|
newIndexes = (int*)ExternAlloc(sizeof(int)*iNumberOfColumns);
|
|
if (!newIndexes)
|
|
{
|
|
ExternFree(newColumns);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
iCurrent = 0;
|
|
// Copy the first elements of the array
|
|
for (i = recoRange.iwcBegin; i < wispalt->OriginalRecoRange.iwcBegin; i++)
|
|
{
|
|
// Fill with the information from the best result
|
|
newColumns[iCurrent] = vrc->pLatticePath->pElem[i].iStroke;
|
|
newIndexes[iCurrent] = vrc->pLatticePath->pElem[i].iAlt;
|
|
iCurrent++;
|
|
}
|
|
// Copy the existing alternate information
|
|
for (j = 0; j < wispalt->iNumberOfColumns; j++)
|
|
{
|
|
newColumns[iCurrent] = wispalt->pColumnIndex[j];
|
|
newIndexes[iCurrent] = wispalt->pIndexInColumn[j];
|
|
iCurrent++;
|
|
}
|
|
// Copy what follows with the information from the best result
|
|
for (i = wispalt->OriginalRecoRange.iwcBegin + wispalt->OriginalRecoRange.cCount;
|
|
i < recoRange.iwcBegin + recoRange.cCount; i++)
|
|
{
|
|
newColumns[iCurrent] = vrc->pLatticePath->pElem[i].iStroke;
|
|
newIndexes[iCurrent] = vrc->pLatticePath->pElem[i].iAlt;
|
|
iCurrent++;
|
|
}
|
|
ASSERT (iCurrent == iNumberOfColumns);
|
|
|
|
// Swap the arrays
|
|
ExternFree(wispalt->pColumnIndex);
|
|
ExternFree(wispalt->pIndexInColumn);
|
|
wispalt->pColumnIndex = newColumns;
|
|
wispalt->pIndexInColumn = newIndexes;
|
|
wispalt->iLength = iCurrent;
|
|
wispalt->iNumberOfColumns = iCurrent;
|
|
return hr;
|
|
}
|
|
|
|
// GetAlternateList
|
|
//
|
|
// This function returns alternates of the best result
|
|
//
|
|
// Parameters:
|
|
// hrc [in] : The handle to the reco context
|
|
// pRecoRange [in, out] : Pointer to a RECO_RANGE that contains the range we want to get
|
|
// the alternates for. This range comes bck modified to
|
|
// reflect the range we actually used.
|
|
// pSize [in, out] : The number of alternates. If phrcalt is NULL then this function returns
|
|
// the number of alternates it can return - Note we may return an arbitrary
|
|
// number with an HRESULT S_FALSE if we think that the number of alternate
|
|
// is too long to compute.
|
|
// phrcalt [out] : Array of alternates used to return the alternate list
|
|
// iBreak [in] : Mode for querying alternates: ALT_BREAKS_SAME, ALT_BREAKS_FULL or ALT_BREAKS_UNIQUE
|
|
/////////////////
|
|
HRESULT WINAPI GetAlternateList(HRECOCONTEXT hrc, RECO_RANGE* pRecoRange, ULONG*pSize, HRECOALT *phrcalt, ALT_BREAKS iBreak)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
struct WispContext *wisphrc;
|
|
VRC *vrc = NULL;
|
|
RECO_RANGE recoRange, widestRecoRange;
|
|
ULONG i = 0;
|
|
ULONG iAlt;
|
|
int j = 0;
|
|
int iColumnIndex = 0;
|
|
FLOAT fCurrentScore = 0.0;
|
|
BOOL bSomethingAdded = FALSE;
|
|
AltRank *pCur = NULL, *pTemp1 = NULL, *pTemp2 = NULL;
|
|
ULONG ulAltCountSameStrokeCount = 0;
|
|
AltRankList altRankList;
|
|
int iFirstStroke = 0;
|
|
int iLastStroke = 0;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (pRecoRange != NULL && IsBadReadPtr(pRecoRange, sizeof(RECO_RANGE)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsBadWritePtr(pSize, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (phrcalt && IsBadWritePtr(phrcalt, (*pSize) * sizeof(*phrcalt)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (iBreak != ALT_BREAKS_FULL &&
|
|
// iBreak != LARGE_BREAKS &&
|
|
iBreak != ALT_BREAKS_UNIQUE &&
|
|
iBreak != ALT_BREAKS_SAME)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
vrc = (VRC*)wisphrc->hrc;
|
|
if (!vrc || !vrc->pLatticePath)
|
|
{
|
|
// There is no ink
|
|
*pSize = 0;
|
|
return TPC_E_NOT_RELEVANT;
|
|
}
|
|
|
|
// Check the reco range to see if it is valid
|
|
if (pRecoRange)
|
|
{
|
|
recoRange = *pRecoRange;
|
|
if (!recoRange.cCount) return E_INVALIDARG;
|
|
if (recoRange.iwcBegin + recoRange.cCount > (ULONG)vrc->pLatticePath->nChars)
|
|
return E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
recoRange.iwcBegin = 0;
|
|
recoRange.cCount = vrc->pLatticePath->nChars;
|
|
}
|
|
|
|
// First trim spaces (if any) from the beginning and end of the reco range
|
|
while (recoRange.cCount > 0 && vrc->pLatticePath->pElem[recoRange.iwcBegin].iAlt == SPACE_ALT_ID)
|
|
{
|
|
recoRange.iwcBegin++;
|
|
recoRange.cCount--;
|
|
}
|
|
|
|
while (recoRange.cCount > 0 && vrc->pLatticePath->pElem[recoRange.iwcBegin + recoRange.cCount - 1].iAlt == SPACE_ALT_ID)
|
|
{
|
|
recoRange.cCount--;
|
|
}
|
|
|
|
// If the range only contained spaces, then return an error
|
|
if (recoRange.cCount == 0)
|
|
{
|
|
return TPC_E_NOT_RELEVANT;
|
|
}
|
|
|
|
// If there aren't any results, error
|
|
if (!vrc->pLatticePath->nChars)
|
|
{
|
|
*pSize = 0;
|
|
return TPC_E_NOT_RELEVANT;
|
|
}
|
|
|
|
// If we are passed a buffer for alternates but it has size zero, just
|
|
// return immediately. (Some of the code for handling alternates doesn't
|
|
// work with a buffer size of zero.)
|
|
if (phrcalt != NULL && *pSize == 0)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// If we're in single segmentation mode and have asked for full breaks, then
|
|
// switch to same breaks.
|
|
if ((wisphrc->dwFlags & RECOFLAG_SINGLESEG) != 0 && iBreak == ALT_BREAKS_FULL)
|
|
{
|
|
iBreak = ALT_BREAKS_SAME;
|
|
}
|
|
|
|
widestRecoRange = recoRange;
|
|
if (iBreak == ALT_BREAKS_FULL || iBreak == LARGE_BREAKS)
|
|
{
|
|
// Get the number of alternates
|
|
if (!phrcalt)
|
|
{
|
|
*pSize = 50; //We need to find a way how to calculate the real count easily...
|
|
return S_FALSE;
|
|
}
|
|
iLastStroke = vrc->pLatticePath->pElem[recoRange.iwcBegin+recoRange.cCount-1].iStroke;
|
|
iFirstStroke = vrc->pLatticePath->pElem[recoRange.iwcBegin].iStroke -
|
|
vrc->pLatticePath->pElem[recoRange.iwcBegin].nStrokes + 1;
|
|
|
|
hr = GetDifSegAltList(vrc, iLastStroke, iFirstStroke, *pSize, &altRankList, iBreak, recoRange.iwcBegin, recoRange.iwcBegin+recoRange.cCount-1);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
// Copy the info from the list to the array
|
|
*pSize = altRankList.ulSize;
|
|
|
|
if (altRankList.ulSize == 0) return S_OK;
|
|
|
|
// TBD: Adjust the alternates to fit the "widest" returned
|
|
// alternate
|
|
// First find the widest alternate (widestRecoRange)
|
|
if (iBreak == LARGE_BREAKS)
|
|
{
|
|
pCur = altRankList.pFirst;
|
|
widestRecoRange = pCur->wispalt->OriginalRecoRange;
|
|
for (i = 0; i < *pSize; i++)
|
|
{
|
|
// Check the start point
|
|
if (widestRecoRange.iwcBegin > pCur->wispalt->OriginalRecoRange.iwcBegin)
|
|
{
|
|
widestRecoRange.cCount += widestRecoRange.iwcBegin -
|
|
pCur->wispalt->OriginalRecoRange.iwcBegin;
|
|
widestRecoRange.iwcBegin = pCur->wispalt->OriginalRecoRange.iwcBegin;
|
|
}
|
|
// Check the end point
|
|
if (widestRecoRange.iwcBegin + widestRecoRange.cCount <
|
|
pCur->wispalt->OriginalRecoRange.iwcBegin +
|
|
pCur->wispalt->OriginalRecoRange.cCount)
|
|
{
|
|
widestRecoRange.cCount = pCur->wispalt->OriginalRecoRange.iwcBegin
|
|
+ pCur->wispalt->OriginalRecoRange.cCount
|
|
- widestRecoRange.iwcBegin;
|
|
}
|
|
// Go to the next
|
|
pCur = pCur->next;
|
|
}
|
|
pCur = altRankList.pFirst;
|
|
// Then call on each alternate the "fit" function
|
|
for (i = 0; i < *pSize; i++)
|
|
{
|
|
hr = FitAlternateToRecoRange(vrc, pCur->wispalt, widestRecoRange);
|
|
// Go to the next
|
|
pCur = pCur->next;
|
|
}
|
|
}
|
|
pCur = altRankList.pFirst;
|
|
pTemp1 = altRankList.pFirst;
|
|
}
|
|
if (iBreak == ALT_BREAKS_UNIQUE || iBreak == ALT_BREAKS_SAME)
|
|
{
|
|
if (iBreak == ALT_BREAKS_UNIQUE && !phrcalt)
|
|
{
|
|
// Get the number of alternates
|
|
*pSize = 50; //We need to find a way how to calculate the real count easily... Impossible?
|
|
|
|
// If we're pretending there is only one segmentation...
|
|
if (wisphrc->dwFlags & RECOFLAG_SINGLESEG)
|
|
{
|
|
*pSize = 1;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
if (!vrc->pLatticePath)
|
|
{
|
|
*pSize = 0;
|
|
return hr;
|
|
}
|
|
|
|
// Get the number of alternates
|
|
if (!phrcalt)
|
|
{
|
|
*pSize = 1;
|
|
for (i = recoRange.iwcBegin; i < recoRange.iwcBegin + recoRange.cCount; i++)
|
|
{
|
|
iColumnIndex = vrc->pLatticePath->pElem[i].iStroke;
|
|
// We need to get the number of alternates with the same number of strokes!!!
|
|
ulAltCountSameStrokeCount = 0;
|
|
for (j = 0; j < vrc->pLattice->pAltList[iColumnIndex].nUsed; j++)
|
|
{
|
|
if (vrc->pLattice->pAltList[iColumnIndex].alts[j].nStrokes ==
|
|
vrc->pLatticePath->pElem[i].nStrokes)
|
|
ulAltCountSameStrokeCount++;
|
|
}
|
|
*pSize *= ulAltCountSameStrokeCount;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// If we're in single segmentation mode and have asked for unique breaks, then
|
|
// we just want to return the best path. Easiest way to do this is to
|
|
// ask for same breaks with one alternate.
|
|
if ((wisphrc->dwFlags & RECOFLAG_SINGLESEG) != 0 && iBreak == ALT_BREAKS_UNIQUE)
|
|
{
|
|
if (*pSize > 1)
|
|
{
|
|
*pSize = 1;
|
|
}
|
|
iBreak = ALT_BREAKS_SAME;
|
|
}
|
|
|
|
// Get the alternates
|
|
// Create a list of alternates
|
|
iLastStroke = vrc->pLatticePath->pElem[recoRange.iwcBegin+recoRange.cCount-1].iStroke;
|
|
iFirstStroke = vrc->pLatticePath->pElem[recoRange.iwcBegin].iStroke -
|
|
vrc->pLatticePath->pElem[recoRange.iwcBegin].nStrokes + 1;
|
|
|
|
hr = GetDifSegAltList(vrc, iLastStroke, iFirstStroke, *pSize, &altRankList, iBreak, recoRange.iwcBegin, recoRange.iwcBegin+recoRange.cCount-1);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
// Copy the info from the list to the array
|
|
*pSize = altRankList.ulSize;
|
|
pCur = altRankList.pFirst;
|
|
pTemp1 = altRankList.pFirst;
|
|
|
|
if (altRankList.ulSize == 0) return S_OK;
|
|
}
|
|
for (i = 0; i < *pSize; i++)
|
|
{
|
|
// Allocate the memory for the pRecoRange array
|
|
pCur->wispalt->OriginalRecoRange = recoRange;
|
|
pCur->wispalt->hrc = hrc;
|
|
|
|
// create a tpg handle
|
|
phrcalt[i] = (HRECOALT)CreateTpgHandle(TPG_HRECOALT, pCur->wispalt);
|
|
|
|
// if we fail we'll have to destroy all the other handles
|
|
if (phrcalt[i] == NULL)
|
|
{
|
|
for (iAlt = 0; iAlt < i; iAlt++)
|
|
{
|
|
DestroyTpgHandle(phrcalt[iAlt], TPG_HRECOALT);
|
|
}
|
|
// And also destroy the alternate list
|
|
while (pTemp1)
|
|
{
|
|
pTemp2 = pTemp1->next;
|
|
DestroyAlternateInternal(pTemp1->wispalt);
|
|
ExternFree(pTemp1);
|
|
pTemp1 = pTemp2;
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Go to the next
|
|
pCur = pCur->next;
|
|
}
|
|
|
|
// Get rid of the linked list which previous held all the alternates
|
|
while (pTemp1)
|
|
{
|
|
pTemp2 = pTemp1->next;
|
|
ExternFree(pTemp1);
|
|
pTemp1 = pTemp2;
|
|
}
|
|
|
|
if (pRecoRange)
|
|
*pRecoRange = widestRecoRange;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT WINAPI Process(HRECOCONTEXT hrc, BOOL *pbPartialProcessing)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
HRESULT hr = S_OK;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadWritePtr(pbPartialProcessing, sizeof(BOOL)))
|
|
return E_POINTER;
|
|
|
|
*pbPartialProcessing = FALSE;
|
|
|
|
if (!wisphrc->hrc)
|
|
{
|
|
return S_OK; // There is no ink
|
|
}
|
|
|
|
// We should call the HwxProcess if there is a Guide
|
|
if (wisphrc->bIsBoxed)
|
|
{
|
|
if (wisphrc->bIsCAC)
|
|
{
|
|
hr = EndInkInput(hrc);
|
|
if (FAILED(hr)) return hr;
|
|
wisphrc->bCACEndInk = TRUE;
|
|
}
|
|
if (HwxProcess(wisphrc->hrc))
|
|
{
|
|
// Test wether the result is valid or invalid
|
|
// the result can be invalid if the call to Process was
|
|
// interrupted by a call to AdviseInkChanged
|
|
if (wisphrc->uAbort != (ULONG)((VRC*)wisphrc->hrc)->pLattice->nRealStrokes)
|
|
return TPC_S_INTERRUPTED;
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (HRCR_OK == ProcessHRC(wisphrc->hrc, 0))
|
|
return S_OK;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT WINAPI SetFactoid(HRECOCONTEXT hrc, ULONG cwcFactoid, const WCHAR* pwcFactoid)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
HRESULT hr = S_OK;
|
|
WCHAR *wszOldFactoid;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Save the old factoid so it can be restored if there is an error
|
|
wszOldFactoid = wisphrc->wszFactoid;
|
|
|
|
if (pwcFactoid)
|
|
{
|
|
// validate pointer if not null
|
|
if (IsBadReadPtr (pwcFactoid, cwcFactoid * sizeof (*pwcFactoid)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
wisphrc->wszFactoid = (WCHAR*)ExternAlloc(sizeof(WCHAR)*(cwcFactoid+1));
|
|
if (!wisphrc->wszFactoid)
|
|
{
|
|
wisphrc->wszFactoid = wszOldFactoid;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
memcpy(wisphrc->wszFactoid, pwcFactoid, cwcFactoid * sizeof(WCHAR));
|
|
wisphrc->wszFactoid[cwcFactoid] = 0;
|
|
}
|
|
else
|
|
{
|
|
wisphrc->wszFactoid = NULL;
|
|
}
|
|
|
|
// If we have an HRC already, set the factoid in it.
|
|
if (wisphrc->hrc != NULL)
|
|
{
|
|
switch (SetHwxFactoid(wisphrc->hrc, wisphrc->wszFactoid))
|
|
{
|
|
case HRCR_OK:
|
|
hr = S_OK;
|
|
break;
|
|
case HRCR_UNSUPPORTED:
|
|
hr = TPC_E_INVALID_PROPERTY;
|
|
break;
|
|
case HRCR_CONFLICT:
|
|
hr = TPC_E_OUT_OF_ORDER_CALL;
|
|
break;
|
|
case HRCR_ERROR:
|
|
default:
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Try to create an HRC to test out the factoid setting,
|
|
// then destroy it immediately.
|
|
hr = CreateHRCinContext(wisphrc);
|
|
// Destroy the HRC if we succeeded in creating one.
|
|
if (wisphrc->hrc != NULL)
|
|
{
|
|
if (wisphrc->bIsBoxed)
|
|
HwxDestroy(wisphrc->hrc);
|
|
else
|
|
DestroyHRC(wisphrc->hrc);
|
|
wisphrc->hrc = NULL;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ExternFree(wszOldFactoid);
|
|
}
|
|
else
|
|
{
|
|
ExternFree(wisphrc->wszFactoid);
|
|
wisphrc->wszFactoid = wszOldFactoid;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT WINAPI SetFlags(HRECOCONTEXT hrc, DWORD dwFlags)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
DWORD dwOldFlags;
|
|
HRESULT hr;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
dwOldFlags = wisphrc->dwFlags;
|
|
wisphrc->dwFlags = dwFlags;
|
|
|
|
if (wisphrc->hrc)
|
|
{
|
|
if (SetHwxFlags(wisphrc->hrc, dwFlags))
|
|
hr = S_OK;
|
|
else
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
// Try to create an HRC to set out the flag setting,
|
|
// then destroy it immediately.
|
|
hr = CreateHRCinContext(wisphrc);
|
|
// Destroy the HRC if we succeeded in creating one.
|
|
if (wisphrc->hrc != NULL)
|
|
{
|
|
if (wisphrc->bIsBoxed)
|
|
HwxDestroy(wisphrc->hrc);
|
|
else
|
|
DestroyHRC(wisphrc->hrc);
|
|
wisphrc->hrc = NULL;
|
|
}
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
wisphrc->dwFlags = dwOldFlags;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT WINAPI IsStringSupported(HRECOCONTEXT hrc, ULONG cwcString, const WCHAR *pwcString)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
WCHAR *tempBuffer;
|
|
ULONG ulSize = 0;
|
|
HRESULT hr = S_OK;
|
|
BOOL fCreatedHRC = FALSE;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadReadPtr(pwcString, cwcString * sizeof(*pwcString)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// We need to have a valid HRC to check the factoid
|
|
if (!wisphrc->hrc)
|
|
{
|
|
hr = CreateHRCinContext(wisphrc);
|
|
if (FAILED(hr))
|
|
{
|
|
// Failure might mean we actually made the
|
|
// HRC, but a flag setting or factoid setting failed.
|
|
// So destroy the HRC if it was created.
|
|
if (wisphrc->hrc != NULL)
|
|
{
|
|
if (wisphrc->bIsBoxed)
|
|
HwxDestroy(wisphrc->hrc);
|
|
else
|
|
DestroyHRC(wisphrc->hrc);
|
|
wisphrc->hrc = NULL;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
fCreatedHRC = TRUE;
|
|
}
|
|
|
|
tempBuffer = (WCHAR *) ExternAlloc((cwcString + 1) * sizeof(*tempBuffer));
|
|
if (!tempBuffer)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(tempBuffer, pwcString, cwcString * sizeof(WCHAR));
|
|
tempBuffer[cwcString] = 0;
|
|
|
|
if (IsWStringSupportedHRC(wisphrc->hrc, tempBuffer))
|
|
hr = S_OK;
|
|
else
|
|
hr = S_FALSE;
|
|
ExternFree(tempBuffer);
|
|
|
|
if (fCreatedHRC)
|
|
{
|
|
if (wisphrc->bIsBoxed)
|
|
HwxDestroy(wisphrc->hrc);
|
|
else
|
|
DestroyHRC(wisphrc->hrc);
|
|
wisphrc->hrc = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
int _cdecl CompareWCHAR(const void *arg1, const void *arg2)
|
|
{
|
|
int wch1 = *((WCHAR *) arg1);
|
|
int wch2 = *((WCHAR *) arg2);
|
|
return (wch1 - wch2);
|
|
}
|
|
|
|
// GetUnicodeRanges
|
|
//
|
|
// Parameters:
|
|
// hrec [in] : Handle to the recognizer
|
|
// pcRanges [in/out] : Count of ranges
|
|
// pcr [out] : Array of character ranges
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT WINAPI GetUnicodeRanges(HRECOGNIZER hrec,
|
|
ULONG *pcRanges,
|
|
CHARACTER_RANGE *pcr)
|
|
{
|
|
struct WispRec *pRec;
|
|
WCHAR *aw;
|
|
int cChar, i;
|
|
ULONG cRange, iRange = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
// Check the recognizer handle
|
|
pRec = (struct WispRec*)FindTpgHandle((HANDLE)hrec, TPG_HRECOGNIZER);
|
|
if (NULL == pRec)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadWritePtr(pcRanges, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (pcr != NULL && IsBadWritePtr(pcr, sizeof(CHARACTER_RANGE) * (*pcRanges)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
cChar = g_locRunInfo.cCodePoints - 1;
|
|
aw = (WCHAR *) ExternAlloc(cChar * sizeof(WCHAR));
|
|
if (!aw)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
for (i = 1; i < g_locRunInfo.cCodePoints; i++)
|
|
{
|
|
aw[i - 1] = LocRunDense2Unicode(&g_locRunInfo, (WCHAR) i);
|
|
}
|
|
|
|
if (cChar == 0)
|
|
{
|
|
cRange = 0;
|
|
}
|
|
else
|
|
{
|
|
qsort((void*)aw, (size_t)cChar, sizeof(WCHAR), CompareWCHAR);
|
|
|
|
// count the ranges
|
|
cRange = 1;
|
|
for (i = 1; i < cChar; i++)
|
|
{
|
|
if (aw[i] > aw[i - 1] + 1)
|
|
cRange++;
|
|
}
|
|
}
|
|
|
|
|
|
if (!pcr) // Need only a count of ranges
|
|
{
|
|
*pcRanges = cRange;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (*pcRanges < cRange)
|
|
{
|
|
hr = TPC_E_INSUFFICIENT_BUFFER;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (*pcRanges > cRange)
|
|
{
|
|
*pcRanges = cRange;
|
|
}
|
|
|
|
if (*pcRanges > 0)
|
|
{
|
|
// convert the array of Unicode values to an array of CHARACTER_RANGEs
|
|
pcr[iRange].wcLow = aw[0];
|
|
pcr[iRange].cChars = 1;
|
|
for (i = 1; i < cChar; i++)
|
|
{
|
|
if (aw[i] == aw[i - 1] + 1)
|
|
pcr[iRange].cChars++;
|
|
else
|
|
{
|
|
if (iRange >= (*pcRanges) - 1)
|
|
{
|
|
break;
|
|
}
|
|
iRange++;
|
|
pcr[iRange].wcLow = aw[i];
|
|
pcr[iRange].cChars = 1;
|
|
}
|
|
}
|
|
ASSERT(iRange == (*pcRanges) - 1);
|
|
}
|
|
|
|
cleanup:
|
|
ExternFree(aw);
|
|
return hr;
|
|
}
|
|
|
|
// GetEnabledUnicodeRanges
|
|
//
|
|
// Parameters:
|
|
// hrc [in] : Handle to the recognition context
|
|
// pcRanges [in/out] : Count of ranges
|
|
// pcr [out] : Array of character ranges
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT WINAPI GetEnabledUnicodeRanges(HRECOCONTEXT hrc,
|
|
ULONG *pcRanges,
|
|
CHARACTER_RANGE *pcr)
|
|
{
|
|
VRC *pVRC;
|
|
CHARSET charset;
|
|
WCHAR *aw;
|
|
int cChar, i;
|
|
ULONG cRange, iRange = 0;
|
|
HRESULT hr = S_OK;
|
|
BOOL fCreatedHRC = FALSE;
|
|
|
|
struct WispContext *wisphrc;
|
|
|
|
// validate the correpsonding hrc pointer
|
|
wisphrc = (struct WispContext *) FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadWritePtr(pcRanges, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (pcr != NULL && IsBadWritePtr(pcr, sizeof(CHARACTER_RANGE) * *(pcRanges)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
cChar = 0;
|
|
aw = (WCHAR *) ExternAlloc((g_locRunInfo.cCodePoints - 1) * sizeof(WCHAR));
|
|
if (!aw)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// If we have an HRC, the factoid should be set already. If not, we'll have
|
|
// to make one.
|
|
if (wisphrc->hrc == NULL)
|
|
{
|
|
fCreatedHRC = TRUE;
|
|
hr = CreateHRCinContext(wisphrc);
|
|
}
|
|
|
|
pVRC = (VRC *) wisphrc->hrc;
|
|
|
|
if (SUCCEEDED(hr) && pVRC != NULL)
|
|
{
|
|
charset.recmask = pVRC->pLattice->alcFactoid;
|
|
charset.pbAllowedChars = pVRC->pLattice->pbFactoidChars;
|
|
for (i = 1; i < g_locRunInfo.cCodePoints; i++)
|
|
{
|
|
if (IsAllowedChar(&g_locRunInfo, &charset, (WCHAR) i))
|
|
{
|
|
aw[cChar++] = LocRunDense2Unicode(&g_locRunInfo, (WCHAR) i);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fCreatedHRC)
|
|
{
|
|
// Destroy the HRC if we created one.
|
|
if (wisphrc->hrc != NULL)
|
|
{
|
|
if (wisphrc->bIsBoxed)
|
|
HwxDestroy(wisphrc->hrc);
|
|
else
|
|
DestroyHRC(wisphrc->hrc);
|
|
wisphrc->hrc = NULL;
|
|
}
|
|
}
|
|
|
|
if (cChar == 0)
|
|
{
|
|
cRange = 0;
|
|
}
|
|
else
|
|
{
|
|
qsort((void*)aw, (size_t)cChar, sizeof(WCHAR), CompareWCHAR);
|
|
|
|
// count the ranges
|
|
cRange = 1;
|
|
for (i = 1; i < cChar; i++)
|
|
{
|
|
if (aw[i] > aw[i - 1] + 1)
|
|
cRange++;
|
|
}
|
|
}
|
|
|
|
if (!pcr) // Need only a count of ranges
|
|
{
|
|
*pcRanges = cRange;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (*pcRanges < cRange)
|
|
{
|
|
hr = TPC_E_INSUFFICIENT_BUFFER;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (*pcRanges > cRange)
|
|
{
|
|
*pcRanges = cRange;
|
|
}
|
|
|
|
if (*pcRanges > 0)
|
|
{
|
|
// convert the array of Unicode values to an array of CHARACTER_RANGEs
|
|
pcr[iRange].wcLow = aw[0];
|
|
pcr[iRange].cChars = 1;
|
|
for (i = 1; i < cChar; i++)
|
|
{
|
|
if (aw[i] == aw[i - 1] + 1)
|
|
pcr[iRange].cChars++;
|
|
else
|
|
{
|
|
if (iRange >= (*pcRanges) - 1)
|
|
{
|
|
break;
|
|
}
|
|
iRange++;
|
|
pcr[iRange].wcLow = aw[i];
|
|
pcr[iRange].cChars = 1;
|
|
}
|
|
}
|
|
ASSERT(iRange == (*pcRanges) - 1);
|
|
}
|
|
|
|
cleanup:
|
|
ExternFree(aw);
|
|
return hr;
|
|
}
|
|
|
|
// SetEnabledUnicodeRanges
|
|
//
|
|
// Parameters:
|
|
// hrc [in] : Handle to the recognition context
|
|
// pcRanges [in/out] : Count of ranges
|
|
// pcr [out] : Array of character ranges
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
HRESULT WINAPI SetEnabledUnicodeRanges(HRECOCONTEXT hrc,
|
|
ULONG cRanges,
|
|
CHARACTER_RANGE *pcr)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
|
|
// validate the correpsonding hrc pointer
|
|
wisphrc = (struct WispContext *) FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadReadPtr(pcr, sizeof(CHARACTER_RANGE) * cRanges))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
////////////////////////
|
|
// IAlternate
|
|
////////////////////////
|
|
HRESULT WINAPI GetString(HRECOALT hrcalt, RECO_RANGE *pRecoRange, ULONG* pcSize, WCHAR* szString)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
struct WispAlternate *wispalt;
|
|
VRC *vrc = NULL;
|
|
HRESULT hr = S_OK;
|
|
ULONG i = 0, ulSize = 0;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wispalt = (struct WispAlternate*)FindTpgHandle((HANDLE)hrcalt, TPG_HRECOALT);
|
|
if (NULL == wispalt)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// validate the correpsonding hrc pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)wispalt->hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (pRecoRange && IsBadWritePtr(pRecoRange, sizeof(RECO_RANGE)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsBadWritePtr(pcSize, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// if the string pointer is not null, validate it
|
|
if (szString && IsBadWritePtr(szString, (*pcSize) * sizeof (*szString)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
vrc = (VRC*)wisphrc->hrc;
|
|
if (!vrc)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (pRecoRange)
|
|
{
|
|
pRecoRange->iwcBegin = wispalt->OriginalRecoRange.iwcBegin;
|
|
pRecoRange->cCount = wispalt->OriginalRecoRange.cCount;
|
|
}
|
|
|
|
ulSize = (ULONG)wispalt->iLength;
|
|
|
|
if (!szString)
|
|
{
|
|
*pcSize = ulSize;
|
|
return S_OK;
|
|
}
|
|
|
|
if (*pcSize > ulSize)
|
|
{
|
|
*pcSize = ulSize;
|
|
}
|
|
|
|
if (*pcSize < ulSize)
|
|
{
|
|
hr = TPC_S_TRUNCATED;
|
|
}
|
|
|
|
// Fill the string
|
|
for (i = 0; i<*pcSize; i++)
|
|
{
|
|
// Fill the characters in the string
|
|
if (wispalt->pIndexInColumn[i] == SPACE_ALT_ID)
|
|
{
|
|
szString[i] = SYM_SPACE;
|
|
}
|
|
else
|
|
if (vrc->pLattice->pAltList[wispalt->pColumnIndex[i]].alts[wispalt->pIndexInColumn[i]].wChar != SYM_UNKNOWN)
|
|
{
|
|
szString[i] = LocRunDense2Unicode(&g_locRunInfo,
|
|
vrc->pLattice->pAltList[wispalt->pColumnIndex[i]].alts[wispalt->pIndexInColumn[i]].wChar);
|
|
}
|
|
else
|
|
{
|
|
szString[i] = SYM_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT WINAPI GetStrokeRanges(HRECOALT hrcalt, RECO_RANGE* pRecoRange, ULONG* pcRanges, STROKE_RANGE* pStrokeRange)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
RECO_RANGE recoRange;
|
|
struct WispContext *wisphrc;
|
|
struct WispAlternate *wispalt;
|
|
VRC *vrc;
|
|
ULONG ulStrokeCount = 0, ulCurrentIndex = 0;
|
|
ULONG i = 0;
|
|
int j = 0;
|
|
int k = 0;
|
|
ULONG *StrokeArray = NULL;
|
|
ULONG ulStrokeRangesCount = 0;
|
|
ULONG iCurrentStrokeRange = 0;
|
|
|
|
// find the handle and validate the corresponding pointer
|
|
wispalt = (struct WispAlternate*)FindTpgHandle((HANDLE)hrcalt, TPG_HRECOALT);
|
|
if (NULL == wispalt)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// validate the corresponding hrc pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)wispalt->hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
vrc = (VRC *) wisphrc->hrc;
|
|
if (vrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadReadPtr(pRecoRange, sizeof(RECO_RANGE)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsBadWritePtr(pcRanges, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// validate pointer if not null
|
|
if (pStrokeRange && IsBadWritePtr(pStrokeRange, (*pcRanges) * sizeof (*pStrokeRange)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
recoRange = *pRecoRange;
|
|
if (!recoRange.cCount)
|
|
{
|
|
*pcRanges = 0;
|
|
return S_OK;
|
|
}
|
|
if (recoRange.iwcBegin + recoRange.cCount > (ULONG)wispalt->iLength)
|
|
return E_INVALIDARG;
|
|
|
|
// Get the number of strokes
|
|
for (i = recoRange.iwcBegin; i < recoRange.iwcBegin + recoRange.cCount; i++)
|
|
{
|
|
// Make sure we're not looking at a space
|
|
if (wispalt->pIndexInColumn[i] != SPACE_ALT_ID)
|
|
{
|
|
// There might be some stroke that have been merged!
|
|
// We need to examine every single column to know if strokes have been merged
|
|
// (and also how many times)
|
|
for (j = 0; j < vrc->pLattice->pAltList[wispalt->pColumnIndex[i]].alts[wispalt->pIndexInColumn[i]].nStrokes; j++)
|
|
{
|
|
ulStrokeCount += vrc->pLattice->pStroke[wispalt->pColumnIndex[i]-j].iLast -
|
|
vrc->pLattice->pStroke[wispalt->pColumnIndex[i]-j].iOrder + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there aren't any strokes, then exit early.
|
|
// (The rest of this function behaves badly if there are no strokes or no ranges.)
|
|
if (ulStrokeCount == 0)
|
|
{
|
|
*pcRanges = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
// Allocate the array of strokes
|
|
StrokeArray = (ULONG*)ExternAlloc(sizeof(ULONG)*ulStrokeCount);
|
|
if (!StrokeArray)
|
|
{
|
|
ASSERT(StrokeArray);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
// Get the array of strokes
|
|
ulCurrentIndex = 0;
|
|
for (i = recoRange.iwcBegin; i < recoRange.iwcBegin + recoRange.cCount; i++)
|
|
{
|
|
// Make sure we're not looking at a space
|
|
if (wispalt->pIndexInColumn[i] != SPACE_ALT_ID)
|
|
{
|
|
// This loop goes backwards so we get the strokes in order
|
|
for (j = vrc->pLattice->pAltList[wispalt->pColumnIndex[i]].alts[wispalt->pIndexInColumn[i]].nStrokes - 1; j >= 0; j--)
|
|
{
|
|
// There might more than one stroke here
|
|
// because of the possibility of a stroke merge!!!
|
|
for (k = vrc->pLattice->pStroke[wispalt->pColumnIndex[i]-j].iOrder;
|
|
k <= vrc->pLattice->pStroke[wispalt->pColumnIndex[i]-j].iLast;
|
|
k++)
|
|
{
|
|
StrokeArray[ulCurrentIndex] = k;
|
|
ulCurrentIndex++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Sort the array
|
|
SlowSort(StrokeArray, ulStrokeCount);
|
|
// Get the number of STROKE_RANGES needed
|
|
ulStrokeRangesCount = 1;
|
|
for (i = 1; i<ulStrokeCount; i++)
|
|
{
|
|
if (StrokeArray[i-1]+1!=StrokeArray[i]) ulStrokeRangesCount++;
|
|
}
|
|
// Return the count is this is all that is asked
|
|
if (!pStrokeRange)
|
|
{
|
|
*pcRanges = ulStrokeRangesCount;
|
|
ExternFree(StrokeArray);
|
|
return S_OK;
|
|
}
|
|
if (*pcRanges < ulStrokeRangesCount)
|
|
{
|
|
ExternFree(StrokeArray);
|
|
return TPC_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
// Fill in the Strokes
|
|
iCurrentStrokeRange = 0;
|
|
pStrokeRange[iCurrentStrokeRange].iStrokeBegin = StrokeArray[0];
|
|
pStrokeRange[ulStrokeRangesCount-1].iStrokeEnd = StrokeArray[ulStrokeCount-1];
|
|
for(i = 1; i < ulStrokeCount; i++)
|
|
{
|
|
if (StrokeArray[i-1]+1!=StrokeArray[i])
|
|
{
|
|
pStrokeRange[iCurrentStrokeRange].iStrokeEnd = StrokeArray[i-1];
|
|
iCurrentStrokeRange++;
|
|
if (iCurrentStrokeRange == ulStrokeRangesCount) break;
|
|
pStrokeRange[iCurrentStrokeRange].iStrokeBegin = StrokeArray[i];
|
|
}
|
|
}
|
|
*pcRanges = ulStrokeRangesCount;
|
|
ExternFree(StrokeArray);
|
|
return hr;
|
|
}
|
|
|
|
typedef struct AltScore
|
|
{
|
|
float flScore;
|
|
int iAlt;
|
|
BOOL fCurrentPath;
|
|
} AltScore;
|
|
|
|
int __cdecl CompareAltScore(const void *p1, const void *p2)
|
|
{
|
|
const AltScore *pAlt1 = (const AltScore *) p1;
|
|
const AltScore *pAlt2 = (const AltScore *) p2;
|
|
if (pAlt1->fCurrentPath) return -1;
|
|
if (pAlt2->fCurrentPath) return 1;
|
|
if (pAlt1->flScore > pAlt2->flScore) return -1;
|
|
if (pAlt1->flScore < pAlt2->flScore) return 1;
|
|
return 0;
|
|
}
|
|
|
|
HRESULT WINAPI GetSegmentAlternateList(HRECOALT hrcalt, RECO_RANGE* pRecoRange, ULONG *pcAlts, HRECOALT* pAlts)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
struct WispAlternate *wispalt;
|
|
struct WispContext *wisphrc;
|
|
VRC *vrc;
|
|
ULONG ulMaxAlt = 0;
|
|
struct WispAlternate *pWispAlt = NULL;
|
|
int i = 0;
|
|
int j = 0;
|
|
ULONG ulCurrentIndex = 0;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wispalt = (struct WispAlternate*)FindTpgHandle((HANDLE)hrcalt, TPG_HRECOALT);
|
|
if (NULL == wispalt)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// validate the corresponding hrc pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)wispalt->hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
vrc = (VRC *) wisphrc->hrc;
|
|
if (vrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadWritePtr(pRecoRange, sizeof(RECO_RANGE)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsBadWritePtr(pcAlts, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (pAlts && IsBadWritePtr(pAlts, (*pcAlts) * sizeof (*pAlts)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
// Check that the pRecoRange is valid:
|
|
if (pRecoRange->cCount == 0 ||
|
|
pRecoRange->iwcBegin + pRecoRange->cCount > (ULONG)wispalt->iLength)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Get the number of alternates for this character (with the same number of strokes!!!)
|
|
ulMaxAlt = 0;
|
|
|
|
// if this char is a space
|
|
if (wispalt->pIndexInColumn[pRecoRange->iwcBegin] == SPACE_ALT_ID)
|
|
{
|
|
if (!pAlts)
|
|
{
|
|
*pcAlts = 1;
|
|
}
|
|
if (pAlts && *pcAlts > 0)
|
|
{
|
|
pWispAlt = (struct WispAlternate *)ExternAlloc(sizeof(struct WispAlternate));
|
|
if (!pWispAlt)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pWispAlt->hrc = wispalt->hrc;
|
|
pWispAlt->iLength = 1;
|
|
pWispAlt->iNumberOfColumns = 1;
|
|
pWispAlt->OriginalRecoRange = *pRecoRange;
|
|
pWispAlt->pColumnIndex = NULL;
|
|
pWispAlt->pIndexInColumn = NULL;
|
|
pWispAlt->pColumnIndex = ExternAlloc(sizeof(int));
|
|
pWispAlt->pIndexInColumn = ExternAlloc(sizeof(int));
|
|
if (!pWispAlt->pColumnIndex || !pWispAlt->pIndexInColumn)
|
|
{
|
|
// Problem allocating memory, unallocate the array
|
|
HRESULT hrDA = DestroyAlternateInternal(pWispAlt);
|
|
ASSERT(SUCCEEDED(hrDA));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pWispAlt->pColumnIndex[0] = wispalt->pColumnIndex[pRecoRange->iwcBegin];
|
|
pWispAlt->pIndexInColumn[0] = SPACE_ALT_ID;
|
|
|
|
pAlts[0] = (HRECOALT) CreateTpgHandle(TPG_HRECOALT, pWispAlt);
|
|
if (pAlts[0] == NULL)
|
|
{
|
|
// Problem allocating memory, unallocate the array
|
|
HRESULT hrDA = DestroyAlternateInternal(pWispAlt);
|
|
ASSERT(SUCCEEDED(hrDA));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
*pcAlts = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AltScore *pAltScores;
|
|
int iCurrentPath = -1;
|
|
for (i = 0; i < vrc->pLattice->pAltList[wispalt->pColumnIndex[pRecoRange->iwcBegin]].nUsed; i++)
|
|
{
|
|
if (vrc->pLattice->pAltList[wispalt->pColumnIndex[pRecoRange->iwcBegin]].alts[i].nStrokes ==
|
|
vrc->pLatticePath->pElem[pRecoRange->iwcBegin].nStrokes)
|
|
ulMaxAlt++;
|
|
}
|
|
|
|
if (!pAlts)
|
|
{
|
|
*pcAlts = ulMaxAlt;
|
|
return S_OK;
|
|
}
|
|
|
|
pAltScores = (AltScore *) ExternAlloc(sizeof(AltScore) * ulMaxAlt);
|
|
if (pAltScores == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ulCurrentIndex = 0;
|
|
for (i = 0; i < vrc->pLattice->pAltList[wispalt->pColumnIndex[pRecoRange->iwcBegin]].nUsed; i++)
|
|
{
|
|
// Do the stuff only when we have the same number of strokes
|
|
if (vrc->pLattice->pAltList[wispalt->pColumnIndex[pRecoRange->iwcBegin]].alts[i].nStrokes ==
|
|
vrc->pLatticePath->pElem[pRecoRange->iwcBegin].nStrokes)
|
|
{
|
|
pAltScores[ulCurrentIndex].fCurrentPath = vrc->pLattice->pAltList[wispalt->pColumnIndex[pRecoRange->iwcBegin]].alts[i].fCurrentPath;
|
|
pAltScores[ulCurrentIndex].iAlt = i;
|
|
pAltScores[ulCurrentIndex].flScore =
|
|
vrc->pLattice->pAltList[wispalt->pColumnIndex[pRecoRange->iwcBegin]].alts[i].logProbPath;
|
|
// GetScore(vrc->pLattice, wispalt->pColumnIndex[pRecoRange->iwcBegin], i);
|
|
ulCurrentIndex++;
|
|
}
|
|
}
|
|
|
|
qsort(pAltScores, ulMaxAlt, sizeof(AltScore), CompareAltScore);
|
|
|
|
if (*pcAlts>ulMaxAlt) *pcAlts = ulMaxAlt;
|
|
|
|
// Fill in the array of alternates
|
|
for (i = 0; i < (int)(*pcAlts); i++)
|
|
{
|
|
// Create an alternate
|
|
pWispAlt = (struct WispAlternate *)ExternAlloc(sizeof(struct WispAlternate));
|
|
if (!pWispAlt)
|
|
{
|
|
// Problem allocating memory, unallocate the array
|
|
ExternFree(pAltScores);
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
HRESULT hrDA = DestroyAlternate(pAlts[j]);
|
|
ASSERT(SUCCEEDED(hrDA));
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pWispAlt->hrc = wispalt->hrc;
|
|
pWispAlt->iLength = 1;
|
|
pWispAlt->iNumberOfColumns = 1;
|
|
pWispAlt->OriginalRecoRange = *pRecoRange;
|
|
pWispAlt->pColumnIndex = NULL;
|
|
pWispAlt->pIndexInColumn = NULL;
|
|
pWispAlt->pColumnIndex = ExternAlloc(sizeof(int));
|
|
pWispAlt->pIndexInColumn = ExternAlloc(sizeof(int));
|
|
if (!pWispAlt->pColumnIndex || !pWispAlt->pIndexInColumn)
|
|
{
|
|
// Problem allocating memory, unallocate the array
|
|
ExternFree(pAltScores);
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
HRESULT hrDA = DestroyAlternate(pAlts[j]);
|
|
ASSERT(SUCCEEDED(hrDA));
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
pWispAlt->pColumnIndex[0] = wispalt->pColumnIndex[pRecoRange->iwcBegin];
|
|
pWispAlt->pIndexInColumn[0] = pAltScores[i].iAlt;
|
|
pAlts[i] = CreateTpgHandle(TPG_HRECOALT, pWispAlt);
|
|
if (pAlts[i] == NULL)
|
|
{
|
|
// Problem allocating memory, unallocate the array
|
|
ExternFree(pAltScores);
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
HRESULT hrDA = DestroyAlternate(pAlts[j]);
|
|
ASSERT(SUCCEEDED(hrDA));
|
|
}
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
ExternFree(pAltScores);
|
|
}
|
|
pRecoRange->cCount = 1;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT WINAPI GetMetrics(HRECOALT hrcalt, RECO_RANGE* pRecoRange, LINE_METRICS lm, LINE_SEGMENT* pls)
|
|
{
|
|
struct WispAlternate *wispalt;
|
|
struct WispContext *wisphrc;
|
|
VRC *vrc;
|
|
HRESULT hr = S_OK;
|
|
ULONG index = 0;
|
|
LONG lPrevChar = 0;
|
|
float flTotalY = 0;
|
|
int cCharacters = 0;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wispalt = (struct WispAlternate*)FindTpgHandle((HANDLE)hrcalt, TPG_HRECOALT);
|
|
if (NULL == wispalt)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// validate the corresponding hrc pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)wispalt->hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
vrc = (VRC *) wisphrc->hrc;
|
|
if (vrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadWritePtr(pRecoRange, sizeof(RECO_RANGE)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsBadWritePtr(pls, sizeof(LINE_SEGMENT)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (pRecoRange->cCount == 0 ||
|
|
pRecoRange->iwcBegin+pRecoRange->cCount > (ULONG)wispalt->iLength)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// First trim spaces (if any) from the beginning and end of the reco range
|
|
while (pRecoRange->cCount > 0 && wispalt->pIndexInColumn[pRecoRange->iwcBegin] == SPACE_ALT_ID)
|
|
{
|
|
pRecoRange->iwcBegin++;
|
|
pRecoRange->cCount--;
|
|
}
|
|
|
|
while (pRecoRange->cCount > 0 && wispalt->pIndexInColumn[pRecoRange->iwcBegin + pRecoRange->cCount - 1] == SPACE_ALT_ID)
|
|
{
|
|
pRecoRange->cCount--;
|
|
}
|
|
|
|
// If the range only contained spaces, then return an error
|
|
if (pRecoRange->cCount == 0)
|
|
{
|
|
return TPC_E_NOT_RELEVANT;
|
|
}
|
|
|
|
index = 0;
|
|
lPrevChar = 0;
|
|
|
|
// Right now we only work from left to right, top to bottom so we will compare the x coordinates
|
|
// of two consecutive writing boxes to know if we have a new line
|
|
while (index < pRecoRange->cCount)
|
|
{
|
|
int iColumn = wispalt->pColumnIndex[pRecoRange->iwcBegin+index];
|
|
int iIndexInColumn = wispalt->pIndexInColumn[pRecoRange->iwcBegin+index];
|
|
|
|
// Skip over spaces in the range
|
|
if (iIndexInColumn == SPACE_ALT_ID)
|
|
{
|
|
index++;
|
|
continue;
|
|
}
|
|
|
|
// Check if we switched to a new line
|
|
if (index > 0 &&
|
|
lPrevChar > vrc->pLattice->pAltList[iColumn].alts[iIndexInColumn].writingBox.left)
|
|
{
|
|
// Looks like we moved to a new line, modify the reco range to end here and exit the loop.
|
|
pRecoRange->cCount = index;
|
|
// Back up over spaces until we reach the actual end of the previous line
|
|
while (pRecoRange->cCount > 0 && wispalt->pIndexInColumn[pRecoRange->iwcBegin + pRecoRange->cCount - 1] == SPACE_ALT_ID)
|
|
{
|
|
pRecoRange->cCount--;
|
|
}
|
|
break;
|
|
}
|
|
lPrevChar = vrc->pLattice->pAltList[iColumn].alts[iIndexInColumn].writingBox.left;
|
|
|
|
switch(lm)
|
|
{
|
|
case LM_BASELINE:
|
|
case LM_DESCENDER:
|
|
flTotalY += vrc->pLattice->pAltList[iColumn].alts[iIndexInColumn].writingBox.bottom;
|
|
break;
|
|
case LM_MIDLINE:
|
|
flTotalY += (vrc->pLattice->pAltList[iColumn].alts[iIndexInColumn].writingBox.bottom +
|
|
vrc->pLattice->pAltList[iColumn].alts[iIndexInColumn].writingBox.top) / 2;
|
|
break;
|
|
case LM_ASCENDER:
|
|
flTotalY += vrc->pLattice->pAltList[iColumn].alts[iIndexInColumn].writingBox.top;
|
|
break;
|
|
default:
|
|
ASSERT(!lm);
|
|
return E_INVALIDARG;
|
|
}
|
|
index++;
|
|
cCharacters++;
|
|
}
|
|
|
|
// Average the y positions
|
|
pls->PtA.y = (LONG) (flTotalY / cCharacters);
|
|
pls->PtB.y = pls->PtA.y;
|
|
|
|
// Set the x range based on the updated reco range
|
|
pls->PtA.x = vrc->pLattice->pAltList[wispalt->pColumnIndex[pRecoRange->iwcBegin]].alts[wispalt->pIndexInColumn[pRecoRange->iwcBegin]].writingBox.left;
|
|
pls->PtB.x = vrc->pLattice->pAltList[wispalt->pColumnIndex[pRecoRange->iwcBegin+pRecoRange->cCount-1]].alts[wispalt->pIndexInColumn[pRecoRange->iwcBegin+pRecoRange->cCount-1]].writingBox.right;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT WINAPI GetGuideIndex(HRECOALT hrcalt, RECO_RANGE* pRecoRange, ULONG *piIndex)
|
|
{
|
|
struct WispAlternate *wispalt;
|
|
struct WispContext *wisphrc;
|
|
VRC *vrc;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wispalt = (struct WispAlternate*)FindTpgHandle((HANDLE)hrcalt, TPG_HRECOALT);
|
|
if (NULL == wispalt)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// validate the corresponding hrc pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)wispalt->hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
vrc = (VRC *) wisphrc->hrc;
|
|
if (vrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (IsBadWritePtr(pRecoRange, sizeof(RECO_RANGE)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsBadWritePtr(piIndex, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (!pRecoRange->cCount)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (pRecoRange->iwcBegin + pRecoRange->cCount > (ULONG)wispalt->iLength)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!wisphrc->bIsBoxed)
|
|
{
|
|
return TPC_E_NOT_RELEVANT;
|
|
}
|
|
|
|
// Ok we are clean
|
|
pRecoRange->cCount = 1;
|
|
|
|
// Find the Guide index for this character
|
|
*piIndex = wisphrc->uiGuideIndex;
|
|
*piIndex += vrc->pLattice->pStroke[wispalt->pColumnIndex[pRecoRange->iwcBegin]].iBox;
|
|
|
|
// We should not have any spaces in boxed mode. But just in case, let's try to deal with
|
|
// them in a reasonable way, by assuming the space comes in the box immediately after
|
|
// the box containing the stroke.
|
|
if (wispalt->pIndexInColumn[pRecoRange->iwcBegin] == SPACE_ALT_ID)
|
|
{
|
|
(*piIndex)++;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// If the specified character is a space or on the current path, return medium
|
|
// confidence. Otherwise return poor.
|
|
CONFIDENCE_LEVEL GetConfidenceLevelInternal(VRC *vrc, int iColumn, int iAlt)
|
|
{
|
|
if (iAlt == SPACE_ALT_ID || vrc->pLattice->pAltList[iColumn].alts[iAlt].fCurrentPath)
|
|
{
|
|
return CFL_INTERMEDIATE;
|
|
}
|
|
else
|
|
{
|
|
return CFL_POOR;
|
|
}
|
|
}
|
|
|
|
HRESULT WINAPI GetConfidenceLevel(HRECOALT hrcalt, RECO_RANGE* pRecoRange, CONFIDENCE_LEVEL* pcl)
|
|
{
|
|
struct WispAlternate *wispalt;
|
|
struct WispContext *wisphrc;
|
|
VRC *vrc;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wispalt = (struct WispAlternate*)FindTpgHandle((HANDLE)hrcalt, TPG_HRECOALT);
|
|
if (NULL == wispalt)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// validate the corresponding hrc pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)wispalt->hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
vrc = (VRC *) wisphrc->hrc;
|
|
|
|
// Check the parameters
|
|
if (pRecoRange != NULL)
|
|
{
|
|
if (IsBadWritePtr(pRecoRange, sizeof(RECO_RANGE)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (!pRecoRange->cCount)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (pRecoRange->iwcBegin + pRecoRange->cCount > (ULONG)wispalt->iLength)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
if (IsBadWritePtr(pcl, sizeof(CONFIDENCE_LEVEL)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
#ifndef ENABLE_CONFIDENCE_LEVEL
|
|
|
|
// Ok we are clean
|
|
return E_NOTIMPL;
|
|
|
|
#else
|
|
|
|
if (pRecoRange != NULL)
|
|
{
|
|
// We only return confidence for single characters
|
|
pRecoRange->cCount = 1;
|
|
|
|
// If the specified character is a space or on the current path, return medium
|
|
// confidence. Otherwise return poor.
|
|
*pcl = GetConfidenceLevelInternal(vrc, wispalt->pColumnIndex[pRecoRange->iwcBegin], wispalt->pIndexInColumn[pRecoRange->iwcBegin]);
|
|
}
|
|
else
|
|
{
|
|
// If the first character is a space or on the current path, return medium
|
|
// confidence. Otherwise return poor.
|
|
*pcl = GetConfidenceLevelInternal(vrc, wispalt->pColumnIndex[0], wispalt->pIndexInColumn[0]);
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
#endif
|
|
}
|
|
|
|
HRESULT WINAPI GetPropertyRanges(HRECOALT hrcalt, const GUID *pPropertyGuid, ULONG* pcRanges, RECO_RANGE* pRecoRange)
|
|
{
|
|
struct WispAlternate *wispalt;
|
|
struct WispContext *wisphrc;
|
|
VRC *vrc;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wispalt = (struct WispAlternate*)FindTpgHandle((HANDLE)hrcalt, TPG_HRECOALT);
|
|
if (NULL == wispalt)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// validate the corresponding hrc pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)wispalt->hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
vrc = (VRC *) wisphrc->hrc;
|
|
|
|
if (IsBadReadPtr(pPropertyGuid, sizeof(GUID)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsBadWritePtr(pcRanges, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (pRecoRange != NULL && IsBadWritePtr(pRecoRange, sizeof(RECO_RANGE) * (*pcRanges)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsEqualGUID(pPropertyGuid, &GUID_LINEMETRICS))
|
|
{
|
|
return TPC_E_NOT_RELEVANT;
|
|
}
|
|
|
|
#ifdef ENABLE_CONFIDENCE_LEVEL
|
|
if (IsEqualGUID(pPropertyGuid, &GUID_CONFIDENCELEVEL))
|
|
{
|
|
CONFIDENCE_LEVEL clPrev, clThis;
|
|
int iRange = 0;
|
|
int i;
|
|
for (i = 0; i < wispalt->iLength; i++)
|
|
{
|
|
clThis = GetConfidenceLevelInternal(vrc, wispalt->pColumnIndex[i], wispalt->pIndexInColumn[i]);
|
|
if (i == 0 || (clPrev != clThis))
|
|
{
|
|
iRange++;
|
|
}
|
|
clPrev = clThis;
|
|
}
|
|
if (pRecoRange != NULL && *pcRanges < (ULONG) iRange)
|
|
{
|
|
return TPC_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
*pcRanges = iRange;
|
|
if (pRecoRange != NULL)
|
|
{
|
|
iRange = 0;
|
|
for (i = 0; i < wispalt->iLength; i++)
|
|
{
|
|
clThis = GetConfidenceLevelInternal(vrc, wispalt->pColumnIndex[i], wispalt->pIndexInColumn[i]);
|
|
if (i == 0 || (clPrev != clThis))
|
|
{
|
|
pRecoRange[iRange].iwcBegin = i;
|
|
pRecoRange[iRange].cCount = 1;
|
|
iRange++;
|
|
}
|
|
else
|
|
{
|
|
pRecoRange[iRange - 1].cCount++;
|
|
}
|
|
clPrev = clThis;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
#endif
|
|
|
|
return TPC_E_INVALID_PROPERTY;
|
|
}
|
|
|
|
HRESULT WINAPI GetRangePropertyValue(HRECOALT hrcalt, const GUID *pPropertyGuid, RECO_RANGE* pRecoRange, ULONG*pcbSize, BYTE* pProperty)
|
|
{
|
|
struct WispAlternate *wispalt;
|
|
struct WispContext *wisphrc;
|
|
VRC *vrc;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wispalt = (struct WispAlternate*)FindTpgHandle((HANDLE)hrcalt, TPG_HRECOALT);
|
|
if (NULL == wispalt)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// validate the corresponding hrc pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)wispalt->hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
vrc = (VRC *) wisphrc->hrc;
|
|
|
|
if (IsBadReadPtr(pPropertyGuid, sizeof(GUID)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsBadWritePtr(pRecoRange, sizeof(RECO_RANGE)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsBadWritePtr(pcbSize, sizeof(ULONG)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (pProperty != NULL && IsBadWritePtr(pProperty, *pcbSize))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (IsEqualGUID(pPropertyGuid, &GUID_LINEMETRICS))
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LINE_SEGMENT baseline, midline;
|
|
if (pProperty == NULL)
|
|
{
|
|
*pcbSize = sizeof(LATTICE_METRICS);
|
|
}
|
|
else if (*pcbSize < sizeof(LATTICE_METRICS))
|
|
{
|
|
return TPC_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
*pcbSize = sizeof(LATTICE_METRICS);
|
|
hr = GetMetrics(hrcalt, pRecoRange, LM_BASELINE, &baseline);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = GetMetrics(hrcalt, pRecoRange, LM_MIDLINE, &midline);
|
|
}
|
|
if (SUCCEEDED(hr) && pProperty != NULL)
|
|
{
|
|
LATTICE_METRICS *plm = (LATTICE_METRICS *) pProperty;
|
|
plm->lsBaseline = baseline;
|
|
plm->iMidlineOffset = (short)(midline.PtA.y - baseline.PtA.y);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
#ifdef ENABLE_CONFIDENCE_LEVEL
|
|
if (IsEqualGUID(pPropertyGuid, &GUID_CONFIDENCELEVEL))
|
|
{
|
|
CONFIDENCE_LEVEL cl;
|
|
int iStart = pRecoRange->iwcBegin;
|
|
int iLimit = pRecoRange->iwcBegin + pRecoRange->cCount;
|
|
int i;
|
|
if (pProperty == NULL)
|
|
{
|
|
*pcbSize = sizeof(CONFIDENCE_LEVEL);
|
|
}
|
|
else if (*pcbSize < sizeof(CONFIDENCE_LEVEL))
|
|
{
|
|
return TPC_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
*pcbSize = sizeof(CONFIDENCE_LEVEL);
|
|
cl = GetConfidenceLevelInternal(vrc, wispalt->pColumnIndex[iStart],
|
|
wispalt->pIndexInColumn[iStart]);
|
|
for (i = iStart + 1; i < iLimit; i++)
|
|
{
|
|
if (GetConfidenceLevelInternal(vrc, wispalt->pColumnIndex[i], wispalt->pIndexInColumn[i]) != cl)
|
|
{
|
|
pRecoRange->cCount = i - iStart;
|
|
break;
|
|
}
|
|
}
|
|
if (pProperty != NULL)
|
|
{
|
|
*((CONFIDENCE_LEVEL *) pProperty) = cl;
|
|
}
|
|
return S_OK;
|
|
}
|
|
#endif
|
|
|
|
return TPC_E_INVALID_PROPERTY;
|
|
}
|
|
|
|
void SortLatticeElements(RECO_LATTICE_ELEMENT *pStartElement, RECO_LATTICE_ELEMENT *pCurrent)
|
|
{
|
|
RECO_LATTICE_ELEMENT rleTemp;
|
|
BOOL bSwap = TRUE;
|
|
int iElementCount = pCurrent - pStartElement;
|
|
int i = 0, count = 0;
|
|
|
|
// For now just a quick bubble sort
|
|
count = 1;
|
|
while (bSwap)
|
|
{
|
|
bSwap = FALSE;
|
|
for (i = 0; i < iElementCount-count; i++)
|
|
{
|
|
if (pStartElement[i].score > pStartElement[i+1].score)
|
|
{
|
|
// swap
|
|
rleTemp = pStartElement[i];
|
|
pStartElement[i] = pStartElement[i+1];
|
|
pStartElement[i+1] = rleTemp;
|
|
bSwap = TRUE;
|
|
}
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
|
|
// GetLatticePtr
|
|
//
|
|
// Description: This method creates a Lattice structure for
|
|
// the recognition context and returns a pointer to it. The
|
|
// structure is going to be freed when the recognition
|
|
// context is destoyed or when a new call to GetLatticePtr
|
|
// is issued.
|
|
HRESULT WINAPI GetLatticePtr(HRECOCONTEXT hrc, RECO_LATTICE **ppLattice)
|
|
{
|
|
BOOL fNextColumnSpace = FALSE;
|
|
HRESULT hr = S_OK;
|
|
struct WispContext *wisphrc;
|
|
RECO_LATTICE_ELEMENT *pCurrent = NULL, *pStartElement = NULL, rleInPath;
|
|
int *pMapToLatticeColumn = NULL;
|
|
VRC *vrc = NULL;
|
|
int i = 0, j = 0, k = 0;
|
|
ULONG ulElementCount = 0, ulRealElementCount = 0;
|
|
ULONG ulMaxStroke = 0;
|
|
ULONG *pCurrentStroke = NULL;
|
|
ULONG ulBestResultIndex = 0;
|
|
RECO_LATTICE_ELEMENT *pCur = NULL;
|
|
int iStroke;
|
|
int iExternalColumn;
|
|
BYTE *pCurrentPropertyValue = NULL;
|
|
RECO_LATTICE_PROPERTY *pCurrentProperty = NULL;
|
|
RECO_LATTICE_PROPERTY *pConfidencePropStart = NULL;
|
|
RECO_LATTICE_PROPERTY **ppCurrentProperty = NULL;
|
|
LATTICE_METRICS *pLatticeMetrics;
|
|
FLOAT flScore;
|
|
|
|
// validate and destroy the handle & return the pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (NULL == wisphrc)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Check the parameters
|
|
if (IsBadWritePtr(ppLattice, sizeof(RECO_LATTICE*)))
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
*ppLattice = NULL;
|
|
|
|
// We should only do this if the lattice is dirty!
|
|
if (wisphrc->pLattice)
|
|
{
|
|
hr = FreeRecoLattice(wisphrc);
|
|
ASSERT(SUCCEEDED(hr));
|
|
hr = S_OK;
|
|
}
|
|
|
|
// Do we have results?
|
|
if (!wisphrc->hrc)
|
|
{
|
|
return TPC_E_NOT_RELEVANT;
|
|
}
|
|
|
|
vrc = (VRC*) wisphrc->hrc;
|
|
if (!vrc->pLatticePath)
|
|
{
|
|
return TPC_E_NOT_RELEVANT;
|
|
}
|
|
|
|
// Allocate the lattice
|
|
wisphrc->pLattice = (RECO_LATTICE*)ExternAlloc(sizeof(RECO_LATTICE));
|
|
if (!wisphrc->pLattice)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Initialize the RECO_LATTICE structure
|
|
ZeroMemory(wisphrc->pLattice, sizeof(RECO_LATTICE));
|
|
|
|
wisphrc->pLattice->pGuidProperties = NULL;
|
|
wisphrc->pLattice->ulPropertyCount = 0;
|
|
wisphrc->pLattice->ulColumnCount = vrc->pLattice->nStrokes;
|
|
wisphrc->pLattice->ulBestResultColumnCount = vrc->pLatticePath->nChars;
|
|
|
|
// Add columns to the lattice for each space
|
|
for (i = 0; i < vrc->pLattice->nStrokes; i++)
|
|
{
|
|
if (vrc->pLattice->pAltList[i].fSpaceAfterStroke)
|
|
{
|
|
wisphrc->pLattice->ulColumnCount++;
|
|
}
|
|
}
|
|
|
|
// For now we have only two properties: the GUID_CONFIDENCELEVEL and GUID_LINEMETRICS
|
|
wisphrc->pLattice->ulPropertyCount = 1;
|
|
#ifdef ENABLE_CONFIDENCE_LEVEL
|
|
wisphrc->pLattice->ulPropertyCount++;
|
|
#endif
|
|
wisphrc->pLattice->pGuidProperties = (GUID *) ExternAlloc(wisphrc->pLattice->ulPropertyCount * sizeof(GUID));
|
|
if (wisphrc->pLattice->pGuidProperties == NULL)
|
|
{
|
|
HRESULT hrFRL = FreeRecoLattice(wisphrc);
|
|
ASSERT(SUCCEEDED(hrFRL));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
wisphrc->pLattice->pGuidProperties[0] = GUID_LINEMETRICS;
|
|
#ifdef ENABLE_CONFIDENCE_LEVEL
|
|
wisphrc->pLattice->pGuidProperties[1] = GUID_CONFIDENCELEVEL;
|
|
#endif
|
|
|
|
if (wisphrc->pLattice->ulColumnCount)
|
|
{
|
|
// Allocating the array of LatticeColumns
|
|
wisphrc->pLattice->pLatticeColumns =
|
|
ExternAlloc(wisphrc->pLattice->ulColumnCount * sizeof(RECO_LATTICE_COLUMN));
|
|
if (wisphrc->pLattice->pLatticeColumns)
|
|
{
|
|
ZeroMemory(wisphrc->pLattice->pLatticeColumns,
|
|
wisphrc->pLattice->ulColumnCount * sizeof(RECO_LATTICE_COLUMN));
|
|
}
|
|
// Allocate the arrays for the best result
|
|
wisphrc->pLattice->pulBestResultColumns = (ULONG*)
|
|
ExternAlloc(vrc->pLatticePath->nChars * sizeof(ULONG));
|
|
wisphrc->pLattice->pulBestResultIndexes = (ULONG*)
|
|
ExternAlloc(vrc->pLatticePath->nChars * sizeof(ULONG));
|
|
if (!wisphrc->pLattice->pLatticeColumns ||
|
|
!wisphrc->pLattice->pulBestResultColumns ||
|
|
!wisphrc->pLattice->pulBestResultIndexes)
|
|
{
|
|
HRESULT hrFRL = FreeRecoLattice(wisphrc);
|
|
ASSERT(SUCCEEDED(hrFRL));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Allocating the array of Strokes
|
|
wisphrc->pLattice->pLatticeColumns[0].pStrokes =
|
|
ExternAlloc(vrc->pLattice->nRealStrokes * sizeof(ULONG));
|
|
if (!wisphrc->pLattice->pLatticeColumns[0].pStrokes)
|
|
{
|
|
HRESULT hrFRL = FreeRecoLattice(wisphrc);
|
|
ASSERT(SUCCEEDED(hrFRL));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Do corrections for the merged strokes - Annoying detail!
|
|
j = 0;
|
|
for (i = 0; i < vrc->pLattice->nStrokes; i++)
|
|
{
|
|
if (vrc->pLattice->pStroke[i].iLast != vrc->pLattice->pStroke[i].iOrder)
|
|
{
|
|
//There has been Stroke merging
|
|
// Add the Stroke list for this merge stroke
|
|
for (k = vrc->pLattice->pStroke[i].iOrder; k <= vrc->pLattice->pStroke[i].iLast; k++)
|
|
{
|
|
wisphrc->pLattice->pLatticeColumns[0].pStrokes[j] = k;
|
|
j++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No stroke merging for this stroke
|
|
wisphrc->pLattice->pLatticeColumns[0].pStrokes[j] =
|
|
vrc->pLattice->pStroke[i].iOrder;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
// Count the number of Lattice elements
|
|
for (i = 0; i < vrc->pLattice->nStrokes; i++)
|
|
{
|
|
unsigned int ulElements = vrc->pLattice->pAltList[i].nUsed;
|
|
// For each column count the elements
|
|
if ((wisphrc->dwFlags & RECOFLAG_SINGLESEG) != 0)
|
|
{
|
|
int nStrokes = -1;
|
|
ulElements = 0;
|
|
for (j = 0; j < vrc->pLattice->pAltList[i].nUsed; j++)
|
|
{
|
|
if (vrc->pLattice->pAltList[i].alts[j].fCurrentPath)
|
|
{
|
|
nStrokes = vrc->pLattice->pAltList[i].alts[j].nStrokes;
|
|
break;
|
|
}
|
|
}
|
|
for (j = 0; j < vrc->pLattice->pAltList[i].nUsed; j++)
|
|
{
|
|
if (nStrokes == vrc->pLattice->pAltList[i].alts[j].nStrokes)
|
|
{
|
|
ulElements++;
|
|
}
|
|
}
|
|
}
|
|
ulElementCount += ulElements;
|
|
ulRealElementCount += ulElements;
|
|
// Add an element for each space
|
|
if (vrc->pLattice->pAltList[i].fSpaceAfterStroke)
|
|
{
|
|
ulElementCount++;
|
|
}
|
|
}
|
|
|
|
// Create the elements if needed
|
|
if (ulElementCount)
|
|
{
|
|
wisphrc->pLattice->pLatticeColumns[0].pLatticeElements
|
|
= ExternAlloc(ulElementCount*sizeof(RECO_LATTICE_ELEMENT));
|
|
if (!wisphrc->pLattice->pLatticeColumns[0].pLatticeElements)
|
|
{
|
|
HRESULT hrFRL = FreeRecoLattice(wisphrc);
|
|
ASSERT(SUCCEEDED(hrFRL));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
ZeroMemory(wisphrc->pLattice->pLatticeColumns[0].pLatticeElements,
|
|
ulElementCount*sizeof(RECO_LATTICE_ELEMENT));
|
|
pCurrent = wisphrc->pLattice->pLatticeColumns[0].pLatticeElements;
|
|
|
|
// Let's do the line metrics and the confidence levels
|
|
pCurrentProperty =
|
|
(RECO_LATTICE_PROPERTY *) ExternAlloc((3 + ulRealElementCount) * sizeof(RECO_LATTICE_PROPERTY));
|
|
wisphrc->pLatticeProperties = pCurrentProperty;
|
|
pCurrentPropertyValue =
|
|
(BYTE *) ExternAlloc(3 * sizeof(CONFIDENCE_LEVEL) + ulRealElementCount * sizeof(LATTICE_METRICS));
|
|
wisphrc->pLatticePropertyValues = pCurrentPropertyValue;
|
|
|
|
// Allocate property lists for each element. The non-spaces (real elements) each have two
|
|
// properties, and the spaces have one property.
|
|
#ifdef ENABLE_CONFIDENCE_LEVEL
|
|
ppCurrentProperty =
|
|
(RECO_LATTICE_PROPERTY **) ExternAlloc((ulElementCount + ulRealElementCount) * sizeof(RECO_LATTICE_PROPERTY *));
|
|
#else
|
|
ppCurrentProperty =
|
|
(RECO_LATTICE_PROPERTY **) ExternAlloc(ulRealElementCount * sizeof(RECO_LATTICE_PROPERTY *));
|
|
#endif
|
|
wisphrc->ppLatticeProperties = ppCurrentProperty;
|
|
|
|
if (!pCurrentProperty || !pCurrentPropertyValue || !ppCurrentProperty)
|
|
{
|
|
HRESULT hrFRL = FreeRecoLattice(wisphrc);
|
|
ASSERT(SUCCEEDED(hrFRL));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Fill the RECO_LATTICE_PROPERTY array to contain the confidence level
|
|
pConfidencePropStart = pCurrentProperty;
|
|
|
|
pCurrentProperty->guidProperty = GUID_CONFIDENCELEVEL;
|
|
pCurrentProperty->cbPropertyValue = sizeof(CONFIDENCE_LEVEL);
|
|
pCurrentProperty->pPropertyValue = pCurrentPropertyValue;
|
|
*( (CONFIDENCE_LEVEL*)pCurrentPropertyValue) = CFL_STRONG;
|
|
pCurrentPropertyValue += sizeof(CONFIDENCE_LEVEL);
|
|
pCurrentProperty++;
|
|
|
|
// next value
|
|
pCurrentProperty->guidProperty = GUID_CONFIDENCELEVEL;
|
|
pCurrentProperty->cbPropertyValue = sizeof(CONFIDENCE_LEVEL);
|
|
pCurrentProperty->pPropertyValue = pCurrentPropertyValue;
|
|
*( (CONFIDENCE_LEVEL*)pCurrentPropertyValue) = CFL_INTERMEDIATE;
|
|
pCurrentPropertyValue += sizeof(CONFIDENCE_LEVEL);
|
|
pCurrentProperty++;
|
|
|
|
// next value
|
|
pCurrentProperty->guidProperty = GUID_CONFIDENCELEVEL;
|
|
pCurrentProperty->cbPropertyValue = sizeof(CONFIDENCE_LEVEL);
|
|
pCurrentProperty->pPropertyValue = pCurrentPropertyValue;
|
|
*( (CONFIDENCE_LEVEL*)pCurrentPropertyValue) = CFL_POOR;
|
|
pCurrentPropertyValue += sizeof(CONFIDENCE_LEVEL);
|
|
pCurrentProperty++;
|
|
}
|
|
|
|
// Allocate space for the mapping
|
|
pMapToLatticeColumn = (int *) ExternAlloc(sizeof(int) * vrc->pLattice->nStrokes);
|
|
if (pMapToLatticeColumn == NULL)
|
|
{
|
|
HRESULT hrFRL = FreeRecoLattice(wisphrc);
|
|
ASSERT(SUCCEEDED(hrFRL));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Fill in mapping from internal lattice columns to the newly created lattice columns.
|
|
// Note that this mapping always points to columns related to strokes, not to spaces.
|
|
j = 0;
|
|
for (i = 0; i < vrc->pLattice->nStrokes; i++)
|
|
{
|
|
pMapToLatticeColumn[i] = j;
|
|
j++;
|
|
// Add a column for each space
|
|
if (vrc->pLattice->pAltList[i].fSpaceAfterStroke)
|
|
{
|
|
j++;
|
|
}
|
|
}
|
|
ASSERT( wisphrc->pLattice->ulColumnCount == j );
|
|
|
|
// Initialize the lattice columns
|
|
pCurrentStroke = wisphrc->pLattice->pLatticeColumns[0].pStrokes;
|
|
iExternalColumn = 0;
|
|
for (i = 0; i < vrc->pLattice->nStrokes; i++)
|
|
{
|
|
int iStartStroke = i, iEndStroke = vrc->pLattice->nStrokes;
|
|
ASSERT(pMapToLatticeColumn[i] == iExternalColumn);
|
|
|
|
rleInPath.type = RECO_TYPE_WSTRING;
|
|
ulMaxStroke = 0;
|
|
pStartElement = pCurrent;
|
|
|
|
// If we're pretending to have a single segmentation, then find the next
|
|
// element on the best path, and restrict the search for elements starting
|
|
// here to that column.
|
|
if ((wisphrc->dwFlags & RECOFLAG_SINGLESEG) != 0)
|
|
{
|
|
BOOL fFound = FALSE;
|
|
for (j = i; j < vrc->pLattice->nStrokes && !fFound; j++)
|
|
{
|
|
for (k = 0; k < vrc->pLattice->pAltList[j].nUsed && !fFound; k++)
|
|
{
|
|
if (vrc->pLattice->pAltList[j].alts[k].fCurrentPath)
|
|
{
|
|
// Make sure this element links up to the starting column we
|
|
// are at now. If it doesn't, then the current stroke is in
|
|
// the middle of a best path element, so this column will
|
|
// be empty.
|
|
if (j + 1 - vrc->pLattice->pAltList[j].alts[k].nStrokes != i)
|
|
{
|
|
goto NoElementsInColumn;
|
|
}
|
|
iStartStroke = j;
|
|
iEndStroke = j + 1;
|
|
fFound = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (!fFound)
|
|
{
|
|
goto NoElementsInColumn;
|
|
}
|
|
}
|
|
|
|
// Find all elements that start at this columns #
|
|
// This could probably be optimized
|
|
for (j = iStartStroke; j < iEndStroke; j++)
|
|
{
|
|
// Go through each alt
|
|
for (k = 0; k < vrc->pLattice->pAltList[j].nUsed; k++)
|
|
{
|
|
if (j + 1 - vrc->pLattice->pAltList[j].alts[k].nStrokes == i)
|
|
{
|
|
// This one starts at the right location
|
|
|
|
// Set up the properties. There are two properties for each
|
|
// character, the line metrics and the confidence level.
|
|
pCurrent->epProp.cProperties = 1;
|
|
ASSERT(pCurrent->epProp.apProps == NULL);
|
|
pCurrent->epProp.apProps = ppCurrentProperty;
|
|
|
|
*ppCurrentProperty = pCurrentProperty;
|
|
ppCurrentProperty++;
|
|
|
|
pCurrentProperty->guidProperty = GUID_LINEMETRICS;
|
|
pCurrentProperty->cbPropertyValue = sizeof(LATTICE_METRICS);
|
|
pCurrentProperty->pPropertyValue = pCurrentPropertyValue;
|
|
pCurrentProperty++;
|
|
|
|
pLatticeMetrics = (LATTICE_METRICS *) pCurrentPropertyValue;
|
|
pCurrentPropertyValue += sizeof(LATTICE_METRICS);
|
|
|
|
pLatticeMetrics->lsBaseline.PtA.x = vrc->pLattice->pAltList[j].alts[k].writingBox.left;
|
|
pLatticeMetrics->lsBaseline.PtA.y = vrc->pLattice->pAltList[j].alts[k].writingBox.bottom;
|
|
pLatticeMetrics->lsBaseline.PtB.x = vrc->pLattice->pAltList[j].alts[k].writingBox.right;
|
|
pLatticeMetrics->lsBaseline.PtB.y = vrc->pLattice->pAltList[j].alts[k].writingBox.bottom;
|
|
pLatticeMetrics->iMidlineOffset =
|
|
(SHORT) ((vrc->pLattice->pAltList[j].alts[k].writingBox.top -
|
|
vrc->pLattice->pAltList[j].alts[k].writingBox.bottom) / 2);
|
|
|
|
#ifdef ENABLE_CONFIDENCE_LEVEL
|
|
switch (GetConfidenceLevelInternal(vrc, i, k))
|
|
{
|
|
case CFL_STRONG:
|
|
pCurrent->epProp.cProperties++;
|
|
*ppCurrentProperty = pConfidencePropStart;
|
|
ppCurrentProperty++;
|
|
break;
|
|
case CFL_INTERMEDIATE:
|
|
pCurrent->epProp.cProperties++;
|
|
*ppCurrentProperty = pConfidencePropStart + 1;
|
|
ppCurrentProperty++;
|
|
break;
|
|
case CFL_POOR:
|
|
pCurrent->epProp.cProperties++;
|
|
*ppCurrentProperty = pConfidencePropStart + 2;
|
|
ppCurrentProperty++;
|
|
break;
|
|
}
|
|
#endif
|
|
// Get the character
|
|
if (vrc->pLattice->pAltList[j].alts[k].wChar == SYM_UNKNOWN)
|
|
{
|
|
pCurrent->pData = (void*) SYM_UNKNOWN;
|
|
}
|
|
else
|
|
{
|
|
pCurrent->pData = (void*)(LocRunDense2Unicode(&g_locRunInfo,
|
|
vrc->pLattice->pAltList[j].alts[k].wChar));
|
|
}
|
|
pCurrent->ulNextColumn = pMapToLatticeColumn[j] + 1;
|
|
|
|
// Count up the number of real strokes used by this alternate
|
|
pCurrent->ulStrokeNumber = 0;
|
|
// For each merged stroke in this alternate
|
|
for (iStroke = j; iStroke > j - vrc->pLattice->pAltList[j].alts[k].nStrokes; iStroke--)
|
|
{
|
|
// Add the number of real strokes contained in this merged stroke
|
|
pCurrent->ulStrokeNumber +=
|
|
vrc->pLattice->pStroke[iStroke].iLast -
|
|
vrc->pLattice->pStroke[iStroke].iOrder + 1;
|
|
}
|
|
if (ulMaxStroke < pCurrent->ulStrokeNumber)
|
|
ulMaxStroke = pCurrent->ulStrokeNumber;
|
|
|
|
pCurrent->type = RECO_TYPE_WCHAR;
|
|
flScore = -1024 *
|
|
vrc->pLattice->pAltList[j].alts[k].logProb;
|
|
// GetScore(vrc->pLattice, j, k);
|
|
if (flScore > INT_MAX)
|
|
{
|
|
flScore = (FLOAT) INT_MAX;
|
|
}
|
|
pCurrent->score = (int) flScore;
|
|
// Is it part of the best result?
|
|
if (vrc->pLattice->pAltList[j].alts[k].fCurrentPath)
|
|
{
|
|
// Yes, strore the column
|
|
wisphrc->pLattice->pulBestResultColumns[ulBestResultIndex] = iExternalColumn;
|
|
ASSERT(rleInPath.type == RECO_TYPE_WSTRING);
|
|
rleInPath = *pCurrent;
|
|
}
|
|
pCurrent++;
|
|
}
|
|
}
|
|
}
|
|
// We need to sort that list!
|
|
SortLatticeElements(pStartElement, pCurrent);
|
|
// Is there an element from the best result in this column?
|
|
if (rleInPath.type != RECO_TYPE_WSTRING)
|
|
{
|
|
// find its index in the column
|
|
for (pCur = pStartElement; pCur < pCurrent; pCur++)
|
|
{
|
|
if (!memcmp(pCur, &rleInPath, sizeof(RECO_LATTICE_ELEMENT)))
|
|
break;
|
|
}
|
|
ASSERT(pCur != pCurrent);
|
|
if (pCur != pCurrent)
|
|
{
|
|
wisphrc->pLattice->pulBestResultIndexes[ulBestResultIndex] = pCur - pStartElement;
|
|
ulBestResultIndex++;
|
|
}
|
|
}
|
|
|
|
NoElementsInColumn:
|
|
// Fill in the Reco Column information
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].key = iExternalColumn;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].cpProp.cProperties = 0;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].cpProp.apProps = NULL;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].cStrokes = ulMaxStroke;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].pStrokes = pCurrentStroke;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].cLatticeElements = pCurrent-pStartElement;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].pLatticeElements = pStartElement;
|
|
|
|
// Jump to the next "current" stroke : Always the annoying detail!
|
|
pCurrentStroke += vrc->pLattice->pStroke[i].iLast -
|
|
vrc->pLattice->pStroke[i].iOrder + 1;
|
|
|
|
// The new Start is:
|
|
pStartElement = pCurrent;
|
|
iExternalColumn++;
|
|
|
|
if (vrc->pLattice->pAltList[i].fSpaceAfterStroke)
|
|
{
|
|
// If there is a space, then it must be on the current path (because spaces are only
|
|
// created on the current path). This simplifies the code a lot.
|
|
pStartElement = pCurrent;
|
|
|
|
// Set up the properties. For spaces, the only property that applies is the
|
|
// confidence level.
|
|
pCurrent->epProp.cProperties = 0;
|
|
ASSERT(pCurrent->epProp.apProps == NULL);
|
|
pCurrent->epProp.apProps = ppCurrentProperty;
|
|
#ifdef ENABLE_CONFIDENCE_LEVEL
|
|
switch (GetConfidenceLevelInternal(vrc, i, SPACE_ALT_ID))
|
|
{
|
|
case CFL_STRONG:
|
|
pCurrent->epProp.cProperties++;
|
|
*ppCurrentProperty = pConfidencePropStart;
|
|
ppCurrentProperty++;
|
|
break;
|
|
case CFL_INTERMEDIATE:
|
|
pCurrent->epProp.cProperties++;
|
|
*ppCurrentProperty = pConfidencePropStart + 1;
|
|
ppCurrentProperty++;
|
|
break;
|
|
case CFL_POOR:
|
|
pCurrent->epProp.cProperties++;
|
|
*ppCurrentProperty = pConfidencePropStart + 2;
|
|
ppCurrentProperty++;
|
|
break;
|
|
}
|
|
#endif
|
|
pCurrent->pData = (void*) SYM_SPACE;
|
|
pCurrent->ulNextColumn = iExternalColumn + 1;
|
|
pCurrent->ulStrokeNumber = 0;
|
|
pCurrent->type = RECO_TYPE_WCHAR;
|
|
pCurrent->score = 0;
|
|
wisphrc->pLattice->pulBestResultColumns[ulBestResultIndex] = iExternalColumn;
|
|
wisphrc->pLattice->pulBestResultIndexes[ulBestResultIndex] = 0;
|
|
ulBestResultIndex++;
|
|
|
|
pCurrent++;
|
|
|
|
// Fill in the Reco Column information
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].key = iExternalColumn;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].cpProp.cProperties = 0;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].cpProp.apProps = NULL;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].cStrokes = 0;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].pStrokes = pCurrentStroke;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].cLatticeElements = 1;
|
|
wisphrc->pLattice->pLatticeColumns[iExternalColumn].pLatticeElements = pStartElement;
|
|
|
|
// The new Start is:
|
|
pStartElement = pCurrent;
|
|
iExternalColumn++;
|
|
}
|
|
}
|
|
// Check the number of elements in the best path
|
|
ASSERT(ulBestResultIndex == (ULONG)vrc->pLatticePath->nChars);
|
|
ASSERT(iExternalColumn == wisphrc->pLattice->ulColumnCount);
|
|
ASSERT(pCurrentStroke - wisphrc->pLattice->pLatticeColumns[0].pStrokes == vrc->pLattice->nRealStrokes);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
*ppLattice = wisphrc->pLattice;
|
|
|
|
ExternFree(pMapToLatticeColumn);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Lists of properties
|
|
static const ULONG CONTEXT_PROPERTIES_COUNT = 1;
|
|
|
|
// {1ABC3828-BDF1-4ef3-8F2C-0751EC0DE742}
|
|
static const GUID GUID_ENABLE_IFELANG3 = { 0x1abc3828, 0xbdf1, 0x4ef3, { 0x8f, 0x2c, 0x7, 0x51, 0xec, 0xd, 0xe7, 0x42 } };
|
|
|
|
// GetContextPropertyList
|
|
// Return a list of properties supported on the context
|
|
//
|
|
// Parameters:
|
|
// hrc [in] : Handle to the recognition context
|
|
// pcProperties [in/out] : Number of properties supported
|
|
// pPropertyGUIDS [out] : List of properties supported
|
|
HRESULT WINAPI GetContextPropertyList(HRECOCONTEXT hrc,
|
|
ULONG* pcProperties,
|
|
GUID* pPropertyGUIDS)
|
|
{
|
|
if (NULL == (HRC *)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT) )
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
if ( IsBadWritePtr(pcProperties, sizeof(ULONG)) )
|
|
return E_POINTER;
|
|
if (pPropertyGUIDS == NULL) // Need only the count
|
|
{
|
|
*pcProperties = CONTEXT_PROPERTIES_COUNT;
|
|
return S_OK;
|
|
}
|
|
|
|
if (*pcProperties < CONTEXT_PROPERTIES_COUNT)
|
|
return TPC_E_INSUFFICIENT_BUFFER;
|
|
|
|
*pcProperties = CONTEXT_PROPERTIES_COUNT;
|
|
if ( IsBadWritePtr(pPropertyGUIDS, CONTEXT_PROPERTIES_COUNT * sizeof(GUID)) )
|
|
return E_POINTER;
|
|
|
|
pPropertyGUIDS[0] = GUID_ENABLE_IFELANG3;
|
|
return S_OK;
|
|
}
|
|
|
|
// GetContextPropertyValue
|
|
// Return a property of the context, currently no properties are supported
|
|
//
|
|
// Parameters:
|
|
// hrc [in] : Handle to the recognition context
|
|
// pGuid [in] : Property GUID
|
|
// pcbSize [in/out] : Size of the property buffer (in BYTEs)
|
|
// pProperty [out] : Value of the desired property
|
|
HRESULT WINAPI GetContextPropertyValue(HRECOCONTEXT hrc,
|
|
GUID *pGuid,
|
|
ULONG *pcbSize,
|
|
BYTE *pProperty)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
if ( IsBadReadPtr(pGuid, sizeof(GUID)) )
|
|
return E_POINTER;
|
|
if ( IsBadWritePtr(pcbSize, sizeof(ULONG)) )
|
|
return E_POINTER;
|
|
|
|
if ( IsEqualGUID(pGuid, &GUID_ENABLE_IFELANG3) )
|
|
{
|
|
BOOL *pb = (BOOL *) pProperty;
|
|
if (pProperty == NULL)
|
|
{
|
|
*pcbSize = sizeof(BOOL);
|
|
return S_OK;
|
|
}
|
|
if (*pcbSize < sizeof(BOOL))
|
|
{
|
|
return TPC_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
*pcbSize = sizeof(BOOL);
|
|
#ifdef USE_IFELANG3
|
|
*pb = LatticeIFELang3Available();
|
|
#else
|
|
*pb = FALSE;
|
|
#endif
|
|
return S_OK;
|
|
}
|
|
|
|
return TPC_E_INVALID_PROPERTY;
|
|
}
|
|
|
|
// SetContextPropertyValue
|
|
// Set a property of the context (currently only GUID_ENABLE_IFELANG3)
|
|
//
|
|
// Parameters:
|
|
// hrc [in] : Handle to the recognition context
|
|
// pGuid [in] : Property GUID
|
|
// pcbSize [in] : Size of the property buffer (in BYTEs)
|
|
// pProperty [in] : Value of the desired property
|
|
HRESULT WINAPI SetContextPropertyValue(HRECOCONTEXT hrc,
|
|
GUID *pGuid,
|
|
ULONG cbSize,
|
|
BYTE *pProperty)
|
|
{
|
|
struct WispContext *wisphrc;
|
|
|
|
// find the handle and validate the correpsonding pointer
|
|
wisphrc = (struct WispContext*)FindTpgHandle((HANDLE)hrc, TPG_HRECOCONTEXT);
|
|
if (wisphrc == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
if ( IsBadReadPtr(pGuid, sizeof(GUID)) )
|
|
return E_POINTER;
|
|
if ( IsBadReadPtr(pProperty, cbSize) )
|
|
return E_POINTER;
|
|
|
|
if ( IsEqualGUID(pGuid, &GUID_ENABLE_IFELANG3) )
|
|
{
|
|
BOOL *pb = (BOOL *) pProperty;
|
|
if (cbSize != sizeof(BOOL))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
if (*pb)
|
|
{
|
|
#ifdef USE_IFELANG3
|
|
// If already enabled, return S_FALSE
|
|
if (LatticeIFELang3Available())
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
return LatticeConfigIFELang3() ? S_OK : E_FAIL;
|
|
#else
|
|
return E_FAIL;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef USE_IFELANG3
|
|
// If already disabled, return S_FALSE
|
|
if (!LatticeIFELang3Available())
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
return LatticeUnconfigIFELang3() ? S_OK : E_FAIL;
|
|
#else
|
|
return S_FALSE;
|
|
#endif
|
|
}
|
|
}
|
|
return TPC_E_INVALID_PROPERTY;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// Registration information
|
|
//
|
|
//
|
|
|
|
#define FULL_PATH_VALUE L"Recognizer dll"
|
|
#define RECO_LANGUAGES L"Recognized Languages"
|
|
#define RECO_CAPABILITIES L"Recognizer Capability Flags"
|
|
#define RECO_MANAGER_KEY L"CLSID\\{DE815B00-9460-4F6E-9471-892ED2275EA5}\\InprocServer32"
|
|
#define CLSID_KEY L"CLSID"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// DllRegisterServer - Adds entries to the system registry
|
|
|
|
// This recognizer GUID is going to be
|
|
// {6D0087D7-61D2-495f-9293-5B7B1C3FCEAB}
|
|
// Each recognizer HAS to have a different GUID
|
|
|
|
STDAPI DllRegisterServer(void)
|
|
{
|
|
HKEY hKeyReco = NULL;
|
|
HKEY hKeyRecoManager = NULL;
|
|
LONG lRes = 0;
|
|
HKEY hkeyMyReco = NULL;
|
|
DWORD dwLength = 0, dwType = 0, dwSize = 0;
|
|
DWORD dwDisposition;
|
|
WCHAR szRecognizerPath[MAX_PATH];
|
|
WCHAR *RECO_SUBKEY = NULL, *RECOGNIZER_SUBKEY = NULL;
|
|
WCHAR *RECOPROC_SUBKEY = NULL, *RECOCLSID_SUBKEY = NULL;
|
|
RECO_ATTRS recoAttr;
|
|
HRESULT hr = S_OK;
|
|
HRECOGNIZER hrec;
|
|
|
|
if (FAILED(CreateRecognizer(NULL, &hrec)))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
hr = GetRecoAttributes(hrec, &recoAttr);
|
|
if (FAILED(hr))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
if (FAILED(DestroyRecognizer(hrec)))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
if (recoAttr.awLanguageId[0] == MAKELANGID(LANG_JAPANESE, SUBLANG_NEUTRAL))
|
|
{
|
|
RECO_SUBKEY = L"Software\\Microsoft\\TPG\\System Recognizers\\{6D4087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
RECOGNIZER_SUBKEY = L"CLSID\\{6D4087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOPROC_SUBKEY = L"{6D4087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOCLSID_SUBKEY = L"{6D4087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
}
|
|
if (recoAttr.awLanguageId[0] == MAKELANGID(LANG_KOREAN, SUBLANG_NEUTRAL))
|
|
{
|
|
RECO_SUBKEY = L"Software\\Microsoft\\TPG\\System Recognizers\\{6D5087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
RECOGNIZER_SUBKEY = L"CLSID\\{6D5087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOPROC_SUBKEY = L"{6D5087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOCLSID_SUBKEY = L"{6D5087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
}
|
|
if (recoAttr.awLanguageId[0] == MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED))
|
|
{
|
|
RECO_SUBKEY = L"Software\\Microsoft\\TPG\\System Recognizers\\{6D6087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
RECOGNIZER_SUBKEY = L"CLSID\\{6D6087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOPROC_SUBKEY = L"{6D6087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOCLSID_SUBKEY = L"{6D6087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
}
|
|
if (recoAttr.awLanguageId[0] == MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL))
|
|
{
|
|
RECO_SUBKEY = L"Software\\Microsoft\\TPG\\System Recognizers\\{6D7087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
RECOGNIZER_SUBKEY = L"CLSID\\{6D7087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOPROC_SUBKEY = L"{6D7087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOCLSID_SUBKEY = L"{6D7087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
}
|
|
// Write the path to this dll in the registry under
|
|
// the recognizer subkey
|
|
|
|
// Wipe out the previous values
|
|
lRes = RegDeleteKeyW(HKEY_LOCAL_MACHINE, RECO_SUBKEY);
|
|
// Create the new key
|
|
lRes = RegCreateKeyExW(HKEY_LOCAL_MACHINE, RECO_SUBKEY, 0, NULL,
|
|
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyMyReco, &dwDisposition);
|
|
ASSERT(lRes == ERROR_SUCCESS);
|
|
if (lRes != ERROR_SUCCESS)
|
|
{
|
|
if (hkeyMyReco)
|
|
RegCloseKey(hkeyMyReco);
|
|
return E_FAIL;
|
|
}
|
|
// Get the current path
|
|
// Try to get the path of the RecoObj.dll
|
|
// It should be the same as the one for the RecoCom.dll
|
|
dwLength = GetModuleFileNameW((HMODULE)g_hInstanceDllCode, szRecognizerPath, MAX_PATH);
|
|
if (dwLength == 0 || (dwLength == MAX_PATH && szRecognizerPath[MAX_PATH - 1] != 0))
|
|
{
|
|
RegCloseKey(hkeyMyReco);
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Write the path to the dll as a value
|
|
lRes = RegSetValueExW(hkeyMyReco, FULL_PATH_VALUE, 0, REG_SZ,
|
|
(BYTE*)szRecognizerPath, sizeof(WCHAR)*(wcslen(szRecognizerPath)+1));
|
|
ASSERT(lRes == ERROR_SUCCESS);
|
|
if (lRes != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hkeyMyReco);
|
|
return E_FAIL;
|
|
}
|
|
// Add the reco attribute information
|
|
lRes = RegSetValueExW(hkeyMyReco, RECO_LANGUAGES, 0, REG_BINARY,
|
|
(BYTE*)recoAttr.awLanguageId, 64 * sizeof(WORD));
|
|
ASSERT(lRes == ERROR_SUCCESS);
|
|
if (lRes != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hkeyMyReco);
|
|
return E_FAIL;
|
|
}
|
|
lRes = RegSetValueExW(hkeyMyReco, RECO_CAPABILITIES, 0, REG_DWORD,
|
|
(BYTE*)&(recoAttr.dwRecoCapabilityFlags), sizeof(DWORD));
|
|
ASSERT(lRes == ERROR_SUCCESS);
|
|
if (lRes != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hkeyMyReco);
|
|
return E_FAIL;
|
|
}
|
|
|
|
RegCloseKey(hkeyMyReco);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// DllUnregisterServer - Removes entries from the system registry
|
|
|
|
STDAPI DllUnregisterServer(void)
|
|
{
|
|
LONG lRes1 = 0;
|
|
|
|
// get language id
|
|
WCHAR *RECO_SUBKEY = NULL, *RECOGNIZER_SUBKEY = NULL;
|
|
WCHAR *RECOPROC_SUBKEY = NULL, *RECOCLSID_SUBKEY = NULL;
|
|
RECO_ATTRS recoAttr;
|
|
HRESULT hr = S_OK;
|
|
HRECOGNIZER hrec;
|
|
|
|
if (FAILED(CreateRecognizer(NULL, &hrec)))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
hr = GetRecoAttributes(hrec, &recoAttr);
|
|
if (FAILED(hr))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
if (FAILED(DestroyRecognizer(hrec)))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
if (recoAttr.awLanguageId[0] == MAKELANGID(LANG_JAPANESE, SUBLANG_NEUTRAL))
|
|
{
|
|
RECO_SUBKEY = L"Software\\Microsoft\\TPG\\System Recognizers\\{6D4087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
RECOGNIZER_SUBKEY = L"CLSID\\{6D4087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOPROC_SUBKEY = L"{6D4087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOCLSID_SUBKEY = L"{6D4087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
}
|
|
if (recoAttr.awLanguageId[0] == MAKELANGID(LANG_KOREAN, SUBLANG_NEUTRAL))
|
|
{
|
|
RECO_SUBKEY = L"Software\\Microsoft\\TPG\\System Recognizers\\{6D5087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
RECOGNIZER_SUBKEY = L"CLSID\\{6D5087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOPROC_SUBKEY = L"{6D5087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOCLSID_SUBKEY = L"{6D5087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
}
|
|
if (recoAttr.awLanguageId[0] == MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED))
|
|
{
|
|
RECO_SUBKEY = L"Software\\Microsoft\\TPG\\System Recognizers\\{6D6087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
RECOGNIZER_SUBKEY = L"CLSID\\{6D6087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOPROC_SUBKEY = L"{6D6087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOCLSID_SUBKEY = L"{6D6087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
}
|
|
if (recoAttr.awLanguageId[0] == MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL))
|
|
{
|
|
RECO_SUBKEY = L"Software\\Microsoft\\TPG\\System Recognizers\\{6D7087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
RECOGNIZER_SUBKEY = L"CLSID\\{6D7087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOPROC_SUBKEY = L"{6D7087D7-61D2-495f-9293-5B7B1C3FCEAB}\\InprocServer32";
|
|
RECOCLSID_SUBKEY = L"{6D7087D7-61D2-495f-9293-5B7B1C3FCEAB}";
|
|
}
|
|
|
|
// Wipe out the registry information
|
|
lRes1 = RegDeleteKeyW(HKEY_LOCAL_MACHINE, RECO_SUBKEY);
|
|
|
|
// Try to erase the local machine\software\microsoft\tpg\recognizer
|
|
// if necessary (don't care if it fails)
|
|
RegDeleteKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\TPG\\System Recognizers");
|
|
RegDeleteKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\TPG");
|
|
|
|
if (lRes1 != ERROR_SUCCESS && lRes1 != ERROR_FILE_NOT_FOUND)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
return S_OK ;
|
|
}
|
|
|
|
/*************************************************
|
|
* NAME: validateTpgHandle
|
|
*
|
|
* Generic function to validate a pointer obtained from a WISP
|
|
* style handle. For now function checks the memory
|
|
* is writable
|
|
*
|
|
* RETURNS
|
|
* TRUE if the pointer passes a minimal validation
|
|
*
|
|
*************************************************/
|
|
BOOL validateTpgHandle(void *pPtr, int type)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
switch (type)
|
|
{
|
|
case TPG_HRECOCONTEXT:
|
|
{
|
|
if (0 == IsBadWritePtr(pPtr, sizeof(struct WispContext)))
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TPG_HRECOGNIZER:
|
|
{
|
|
if (0 == IsBadWritePtr(pPtr, sizeof(struct WispRec)))
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TPG_HRECOALT:
|
|
{
|
|
if (0 == IsBadWritePtr(pPtr, sizeof(struct WispAlternate)))
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return bRet;
|
|
}
|