屬性 (通用元素)

文字服務架構 (TSF) 提供屬性,讓元數據與文字範圍產生關聯。 這些屬性包括但不限於顯示粗體文字、文字語言標識碼等屬性,以及文字服務所提供的原始數據,例如語音文字服務中與文字相關聯的音頻數據。

下列範例示範如何檢視可能值為紅色 (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”會既是粗體,又是紅色,「有些」會正常顯示,「有色」會是綠色,斜體化,而「文字」則會斜體化。

相同類型的屬性無法重疊。 例如,不允許下列情況,因為 「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 static-compact 屬性對象與靜態屬性物件相同,但 static-compact 屬性不會儲存範圍數據。 當要求靜態壓縮屬性所涵蓋的範圍時,會為每個相鄰屬性群組建立範圍。 靜態壓縮屬性是儲存每個字元屬性的最有效率的方式。 ITfReadOnlyProperty::GetType 會傳回GUID_TFCAT_PROPSTYLE_STATICCOMPACT類別。
自訂 自訂屬性物件會儲存屬性所套用之每個範圍的範圍資訊。 不過,它不會儲存 屬性的實際數據。 相反地,自定義屬性會儲存 ITfPropertyStore 物件。 TSF 管理員會使用此物件來存取及維護屬性數據。ITfReadOnlyProperty::GetType 會傳回GUID_TFCAT_PROPSTYLE_CUSTOM類別。

 

使用屬性

屬性值和屬性是使用 ITfReadOnlyProperty 介面取得,並使用 ITfProperty 介面進行修改

如果需要特定屬性類型,則會 使用 ITfContext::GetPropertyITfContext::GetProperty 需要 GUID 來識別要取得的屬性。 TSF 會定義一組 使用的預先定義屬性識別碼 ,或文字服務可以定義自己的屬性標識符。 如果使用自定義屬性,屬性提供者必須發佈屬性 GUID 和取得之數據的格式。

例如,若要取得文字範圍擁有者的 CLSID,請呼叫 ITfContext::GetProperty 以取得屬性物件,呼叫 ITfProperty::FindRange 以取得完全涵蓋屬性的範圍,然後呼叫 ITfReadOnlyProperty::GetValue 以取得代表擁有文字之文字服務的 CLSID 的 TfGuidAtom。 下列範例示範一個函式,指定內容、範圍和編輯 Cookie,將取得 擁有文字之文字服務的 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 介面,也可以列舉屬性。

屬性的永續性儲存區

屬性通常對應用程式而言是透明的,而且由一或多個文字服務使用。 為了保留屬性數據,例如儲存至檔案時,應用程式必須在儲存屬性數據時串行化屬性數據,並在還原數據時取消串行化屬性數據。 在此情況下,應用程式不應該對個別屬性感興趣,但應該列舉內容中的所有屬性並加以儲存。

儲存屬性數據時,應用程式應該執行下列步驟。

  1. 藉由呼叫 ITfContext::EnumProperties 來取得屬性列舉值。
  2. 呼叫 IEnumTfProperties::Next 來列舉每個屬性。
  3. 針對每個屬性,呼叫 ITfReadOnlyProperty::EnumRanges 來取得範圍列舉值。
  4. 藉由呼叫 IEnumTfRanges::Next,列舉 屬性中的每個範圍。
  5. 針對 屬性中的每個範圍,使用屬性、範圍、TF_PERSISTENT_PROPERTY_HEADER_ACP結構和應用程式所實作的數據流物件,呼叫 ITextStoreACPServices::Serialize
  6. 將TF_PERSISTENT_PROPERTY_HEADER_ACP結構的內容寫入永續性記憶體中。
  7. 將數據流物件的內容寫入永續性記憶體中。
  8. 針對所有屬性中的所有範圍,繼續先前的步驟。
  9. 應用程式應該將某種終止符寫入數據流,以便在還原數據時識別停止點。
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

還原屬性數據時,應用程式應該執行下列步驟。

  1. 將數據流指標設定為第一個 TF_PERSISTENT_PROPERTY_HEADER_ACP 結構的開頭。

  2. 讀取TF_PERSISTENT_PROPERTY_HEADER_ACP結構。

  3. 使用 TF_PERSISTENT_PROPERTY_HEADER_ACP 結構的 guidType 成員呼叫 ITfContext::GetProperty

  4. 應用程式此時可以執行兩項動作之一。

    1. 建立應用程式必須實作的 ITfPersistentPropertyLoaderACP 對象的實例。 然後針對 pStream 和 ITfPersistentPropertyLoaderACP 指標,使用 NULL 呼叫 ITextStoreACPServices::Unserialize。
    2. 將輸入數據流傳遞至 ITextStoreACPServices::UnserializeNULL 以進行 pLoader

    優先使用第一個方法,因為它是最有效率的方法。 實作第二個方法會導致 ITextStoreACPServices::Unserialize 呼叫期間,從數據流讀取所有屬性數據。 第一個方法會在稍後依需求讀取屬性數據。

  5. 重複上述步驟,直到所有屬性區塊都未串行化為止。

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;
}