속성(공통 요소)
TSF(Text Services Framework)는 메타데이터를 텍스트 범위와 연결하는 속성을 제공합니다. 이러한 속성은 굵은 텍스트, 텍스트의 언어 식별자 및 음성 텍스트 서비스의 텍스트와 연결된 오디오 데이터와 같은 텍스트 서비스에서 제공하는 원시 데이터와 같은 특성을 표시하지만 이에 국한되지 않습니다.
다음 예제에서는 가능한 값이 빨강(R), 녹색(G) 또는 파랑(B)인 가상 텍스트 색 속성을 보는 방법을 보여 줍니다.
COLOR: RR GGGGGGGG
TEXT: this is some colored text
다른 형식의 속성은 겹칠 수 있습니다. 예를 들어 이전 예제를 사용하여 굵게(B) 또는 기울임꼴(I)일 수 있는 텍스트 특성을 추가합니다.
ATTRIB:BBBBBBB IIIIIIIIIIII
COLOR: RR GGGGGGGG
TEXT: this is some colored text
텍스트 "this"는 굵게, "is"는 굵게와 빨간색이고, "some"는 정상적으로 표시되고, "colored"는 녹색이고 기울임꼴로 표시되고 "text"는 기울임꼴로 표시됩니다.
동일한 형식의 속성은 겹칠 수 없습니다. 예를 들어 "is" 및 "colored"에는 동일한 형식의 값이 겹치므로 다음 상황이 허용되지 않습니다.
COLOR: GGG GGGG RRR BBBBGGG
COLOR: RR GGGGGGGG
TEXT: this is some colored text
TSF는 세 가지 유형의 속성을 정의합니다.
속성 유형 | 설명 |
---|---|
정적 | 정적 속성 개체는 속성 데이터를 텍스트와 함께 저장합니다. 또한 속성이 적용되는 각 범위에 대한 텍스트 정보 범위도 저장합니다. ITfReadOnlyProperty::GetType은 GUID_TFCAT_PROPSTYLE_STATIC 범주를 반환합니다. |
Static-Compact | 정적 압축 속성 개체는 정적 압축 속성이 범위 데이터를 저장하지 않는다는 점을 제외하고 정적 속성 개체와 동일합니다. 정적 압축 속성이 적용되는 범위를 요청하면 인접 속성의 각 그룹에 대해 범위가 만들어집니다. 정적 압축 속성은 문자별로 속성을 저장하는 가장 효율적인 방법입니다. ITfReadOnlyProperty::GetType은 GUID_TFCAT_PROPSTYLE_STATICCOMPACT 범주를 반환합니다. |
사용자 지정 | 사용자 지정 속성 개체는 속성이 적용되는 각 범위에 대한 범위 정보를 저장합니다. 그러나 속성에 대한 실제 데이터는 저장하지 않습니다. 대신 사용자 지정 속성은 ITfPropertyStore 개체를 저장합니다. TSF 관리자는 이 개체를 사용하여 속성 데이터에 액세스하고 유지 관리합니다. ITfReadOnlyProperty::GetType은 GUID_TFCAT_PROPSTYLE_CUSTOM 범주를 반환합니다. |
속성 값 및 특성은 ITfReadOnlyProperty 인터페이스를 사용하여 가져오고 ITfProperty 인터페이스를 사용하여 수정됩니다.
특정 속성 형식이 필요한 경우 ITfContext::GetProperty 가 사용됩니다. ITfContext::GetProperty에는 가져올 속성을 식별하는 GUID가 필요합니다. TSF는 사용되는 미리 정의된 속성 식별자 집합을 정의하거나 텍스트 서비스에서 자체 속성 식별자를 정의할 수 있습니다. 사용자 지정 속성을 사용하는 경우 속성 공급자는 속성 GUID 및 가져온 데이터의 형식을 게시해야 합니다.
예를 들어 텍스트 범위의 소유자에 대한 CLSID를 가져오려면 ITfContext::GetProperty를 호출하여 속성 개체를 가져오고, ITfProperty::FindRange를 호출하여 속성을 완전히 포함하는 범위를 가져온 다음, ITfReadOnlyProperty::GetValue를 호출하여 텍스트를 소유하는 텍스트 서비스의 CLSID를 나타내는 TfGuidAtom을 가져옵니다. 다음 예제에서는 컨텍스트, 범위 및 편집 쿠키가 지정된 경우 텍스트를 소유하는 텍스트 서비스의 CLSID를 가져오는 함수를 보여 줍니다.
HRESULT GetTextOwner( ITfContext *pContext,
ITfRange *pRange,
TfEditCookie ec,
CLSID *pclsidOwner)
{
HRESULT hr;
ITfProperty *pProp;
*pclsidOwner = GUID_NULL;
hr = pContext->GetProperty(GUID_PROP_TEXTOWNER, &pProp);
if(S_OK == hr)
{
ITfRange *pPropRange;
hr = pProp->FindRange(ec, pRange, &pPropRange, TF_ANCHOR_START);
if(S_OK == hr)
{
VARIANT var;
VariantInit(&var);
hr = pProp->GetValue(ec, pPropRange, &var);
if(S_OK == hr)
{
if(VT_I4 == var.vt)
{
/*
var.lVal is a TfGuidAtom that represents the CLSID of the
text owner. Use ITfCategoryMgr to obtain the CLSID from
the TfGuidAtom.
*/
ITfCategoryMgr *pCatMgr;
hr = CoCreateInstance( CLSID_TF_CategoryMgr,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITfCategoryMgr,
(LPVOID*)&pCatMgr);
if(SUCCEEDED(hr))
{
hr = pCatMgr->GetGUID((TfGuidAtom)var.lVal, pclsidOwner);
if(SUCCEEDED(hr))
{
/*
*pclsidOwner now contains the CLSID of the text
service that owns the text at the selection.
*/
}
pCatMgr->Release();
}
}
else
{
//Unrecognized VARIANT type
hr = E_FAIL;
}
VariantClear(&var);
}
pPropRange->Release();
}
pProp->Release();
}
return hr;
}
ITfContext::EnumProperties에서 IEnumTfProperties 인터페이스를 가져와 속성을 열거할 수도 있습니다.
속성은 애플리케이션에 투명하며 하나 이상의 텍스트 서비스에서 사용되는 경우가 많습니다. 파일에 저장할 때와 같은 속성 데이터를 유지하려면 애플리케이션이 저장될 때 속성 데이터를 직렬화하고 데이터가 복원될 때 속성 데이터를 비직렬화해야 합니다. 이 경우 애플리케이션은 개별 속성에 관심이 없어야 하지만 컨텍스트의 모든 속성을 열거하고 저장해야 합니다.
속성 데이터를 저장할 때 애플리케이션은 다음 단계를 수행해야 합니다.
- ITfContext::EnumProperties를 호출하여 속성 열거자를 가져옵니다.
- IEnumTfProperties::Next를 호출하여 각 속성을 열거합니다.
- 각 속성에 대해 ITfReadOnlyProperty::EnumRanges를 호출하여 범위 열거자를 가져옵니다.
- IEnumTfRanges::Next를 호출하여 속성의 각 범위를 열거합니다.
- 속성의 각 범위에 대해 속성, 범위, TF_PERSISTENT_PROPERTY_HEADER_ACP 구조 및 애플리케이션에서 구현한 스트림 개체를 사용하여 ITextStoreACPServices::Serialize를 호출합니다.
- TF_PERSISTENT_PROPERTY_HEADER_ACP 구조체의 내용을 영구 메모리에 씁니다.
- 스트림 개체의 내용을 영구 메모리에 씁니다.
- 모든 속성의 모든 범위에 대해 이전 단계를 계속합니다.
- 애플리케이션은 데이터가 복원될 때 중지점을 식별할 수 있도록 스트림에 일부 유형의 종결자를 작성해야 합니다.
HRESULT SaveProperties( ITfContext *pContext,
ITextStoreACPServices *pServices,
TfEditCookie ec,
IStream *pStream)
{
HRESULT hr;
IEnumTfProperties *pEnumProps;
TF_PERSISTENT_PROPERTY_HEADER_ACP PropHeader;
ULONG uWritten;
//Enumerate the properties in the context.
hr = pContext->EnumProperties(&pEnumProps);
if(SUCCEEDED(hr))
{
ITfProperty *pProp;
ULONG uFetched;
while(SUCCEEDED(pEnumProps->Next(1, &pProp, &uFetched)) && uFetched)
{
//Enumerate all the ranges that contain the property.
IEnumTfRanges *pEnumRanges;
hr = pProp->EnumRanges(ec, &pEnumRanges, NULL);
if(SUCCEEDED(hr))
{
IStream *pTempStream;
//Create a temporary stream to write the property data to.
hr = CreateStreamOnHGlobal(NULL, TRUE, &pTempStream);
if(SUCCEEDED(hr))
{
ITfRange *pRange;
while(SUCCEEDED(pEnumRanges->Next(1, &pRange, &uFetched)) && uFetched)
{
LARGE_INTEGER li;
//Reset the temporary stream pointer.
li.QuadPart = 0;
pTempStream->Seek(li, STREAM_SEEK_SET, NULL);
//Get the property header and data for the range.
hr = pServices->Serialize(pProp, pRange, &PropHeader, pTempStream);
/*
Write the property header into the primary stream.
The header also contains the size of the property
data.
*/
hr = pStream->Write(&PropHeader, sizeof(PropHeader), &uWritten);
//Reset the temporary stream pointer.
li.QuadPart = 0;
pTempStream->Seek(li, STREAM_SEEK_SET, NULL);
//Copy the property data from the temporary stream into the primary stream.
ULARGE_INTEGER uli;
uli.QuadPart = PropHeader.cb;
hr = pTempStream->CopyTo(pStream, uli, NULL, NULL);
pRange->Release();
}
pTempStream->Release();
}
pEnumRanges->Release();
}
pProp->Release();
}
pEnumProps->Release();
}
//Write a property header with zero size and guid into the stream as a terminator
ZeroMemory(&PropHeader, sizeof(PropHeader));
hr = pStream->Write(&PropHeader, sizeof(PropHeader), &uWritten);
return hr;
}
ITextStoreACPServices::SerializeITfPropertyStore::Serialize
속성 데이터를 복원할 때 애플리케이션은 다음 단계를 수행해야 합니다.
스트림 포인터를 첫 번째 TF_PERSISTENT_PROPERTY_HEADER_ACP 구조체의 시작 부분에 설정합니다.
TF_PERSISTENT_PROPERTY_HEADER_ACP 구조를 읽습니다.
TF_PERSISTENT_PROPERTY_HEADER_ACP 구조체의 guidType 멤버를 사용하여 ITfContext::GetProperty를 호출합니다.
애플리케이션은 이 시점에서 두 가지 중 하나를 수행할 수 있습니다.
- 애플리케이션에서 구현해야 하는 ITfPersistentPropertyLoaderACP 개체의 인스턴스를 만듭니다. 그런 다음, pStream 및 ITfPersistentPropertyLoaderACP 포인터에 대해 NULL을 사용하여 ITextStoreACPServices::Unserialize 를 호출합니다.
- 입력 스트림을 ITextStoreACPServices::Unserialize 및 nULL for pLoader에 전달합니다.
첫 번째 메서드는 가장 효율적이므로 선호됩니다. 두 번째 메서드를 구현하면 ITextStoreACPServices::Unserialize 호출 중에 스트림에서 모든 속성 데이터를 읽을 수 있습니다. 첫 번째 메서드는 나중에 요청 시 속성 데이터를 읽게 합니다.
모든 속성 블록이 비직렬화될 때까지 이전 단계를 반복합니다.
HRESULT LoadProperties( ITfContext *pContext,
ITextStoreACPServices *pServices,
IStream *pStream)
{
HRESULT hr;
ULONG uRead;
TF_PERSISTENT_PROPERTY_HEADER_ACP PropHeader;
/*
Read each property header and property data from the stream. The
list of properties is terminated by a TF_PERSISTENT_PROPERTY_HEADER_ACP
structure with a cb member of zero.
*/
hr = pStream->Read(&PropHeader, sizeof(PropHeader), &uRead);
while( SUCCEEDED(hr) &&
(sizeof(PropHeader) == uRead) &&
(0 != PropHeader.cb))
{
ITfProperty *pProp;
hr = pContext->GetProperty(PropHeader.guidType, &pProp);
if(SUCCEEDED(hr))
{
/*
Have TSF read the property data from the stream. This call
requests a read-only lock, so be sure it can be granted
or else this method will fail.
*/
CTSFPersistentPropertyLoader *pLoader = new CTSFPersistentPropertyLoader(&PropHeader, pStream);
hr = pServices->Unserialize(pProp, &PropHeader, NULL, pLoader);
pProp->Release();
}
//Read the next header.
hr = pStream->Read(&PropHeader, sizeof(PropHeader), &uRead);
}
return hr;
}