如何將内嵌物件新增至文字版面配置

提供簡短教學課程,說明如何使用IDWriteTextLayout介面將内嵌物件新增至DirectWrite應用程式。

本教學課程的最終產品是一個應用程式,其中顯示內嵌影像內嵌影像的文字,如下列螢幕擷取畫面所示。

內嵌影像的「内嵌物件範例」螢幕擷取畫面

本教學課程包含下列部分:

步驟 1:建立文字版面配置。

若要開始,您將需要具有 IDWriteTextLayout 物件的應用程式。 如果您已經有顯示文字配置文字的應用程式,請跳至步驟 2。

若要新增文字配置,您必須執行下列動作:

  1. IDWriteTextLayout 介面的指標宣告為 類別的成員。

    IDWriteTextLayout* pTextLayout_;
    
    
  2. 在 CreateDeviceIndependentResources 方法的結尾,呼叫CreateTextLayout方法建立IDWriteTextLayout介面物件。

    // Create a text layout using the text format.
    if (SUCCEEDED(hr))
    {
        RECT rect;
        GetClientRect(hwnd_, &rect); 
        float width  = rect.right  / dpiScaleX_;
        float height = rect.bottom / dpiScaleY_;
    
        hr = pDWriteFactory_->CreateTextLayout(
            wszText_,      // The string to be laid out and formatted.
            cTextLength_,  // The length of the string.
            pTextFormat_,  // The text format to apply to the string (contains font information, etc).
            width,         // The width of the layout box.
            height,        // The height of the layout box.
            &pTextLayout_  // The IDWriteTextLayout interface pointer.
            );
    }
    
    
  3. 然後,您必須將 ID2D1RenderTarget::D rawText 方法的呼叫變更為 ID2D1RenderTarget::D rawTextLayout,如下列程式碼所示。

    pRT_->DrawTextLayout(
        origin,
        pTextLayout_,
        pBlackBrush_
        );
    
    

步驟 2:定義衍生自 IDWriteInlineObject 介面的類別。

IDWriteInlineObject介面提供DirectWrite内嵌物件的支援。 若要使用内嵌物件,您必須實作這個介面。 它會處理内嵌物件的繪圖,以及提供計量和其他資訊給轉譯器。

建立新的標頭檔並宣告名為 InlineImage 的介面,衍生自 IDWriteInlineObject

除了繼承自 IUnknown 的 QueryInterface、AddRef 和 Release 之外,這個類別必須具有下列方法:

步驟 3:實作內嵌物件類別。

為類別實作建立名為 InlineImage.cpp 的新 C++ 檔案。 除了 LoadBitmapFromFile 方法和繼承自 IUnknown 介面的方法之外,InlineImage 類別還由下列方法所組成。

建構函式。

InlineImage::InlineImage(
    ID2D1RenderTarget *pRenderTarget, 
    IWICImagingFactory *pIWICFactory,
    PCWSTR uri
    )
{
    // Save the render target for later.
    pRT_ = pRenderTarget;

    pRT_->AddRef();

    // Load the bitmap from a file.
    LoadBitmapFromFile(
        pRenderTarget,
        pIWICFactory,
        uri,
        &pBitmap_
        );
}

建構函式的第一個引數是將呈現內嵌影像的 ID2D1RenderTarget。 這會儲存在繪圖時供稍後使用。

轉譯目標 IWICImagingFactory 和檔案名 uri 都會傳遞至 LoadBitmapFromFile 方法,以載入點陣圖,並將點陣圖大小儲存 (寬度和高度) rect_成員變數中。

Draw 方法。

Draw方法是回呼,當需要繪製内嵌物件時,IDWriteTextRenderer物件會呼叫。 文字配置不會直接呼叫此方法。

HRESULT STDMETHODCALLTYPE InlineImage::Draw(
    __maybenull void* clientDrawingContext,
    IDWriteTextRenderer* renderer,
    FLOAT originX,
    FLOAT originY,
    BOOL isSideways,
    BOOL isRightToLeft,
    IUnknown* clientDrawingEffect
    )
{
    float height    = rect_.bottom - rect_.top;
    float width     = rect_.right  - rect_.left;
    D2D1_RECT_F destRect  = {originX, originY, originX + width, originY + height};

    pRT_->DrawBitmap(pBitmap_, destRect);

    return S_OK;
}

在此情況下,會使用 ID2D1RenderTarget::D rawBitmap 方法來繪製點陣圖;不過,可以使用繪製的任何方法。

GetMetrics 方法。

HRESULT STDMETHODCALLTYPE InlineImage::GetMetrics(
    __out DWRITE_INLINE_OBJECT_METRICS* metrics
    )
{
    DWRITE_INLINE_OBJECT_METRICS inlineMetrics = {};
    inlineMetrics.width     = rect_.right  - rect_.left;
    inlineMetrics.height    = rect_.bottom - rect_.top;
    inlineMetrics.baseline  = baseline_;
    *metrics = inlineMetrics;
    return S_OK;
}

針對 GetMetrics 方法,將寬度、高度和基準儲存在 DWRITE_INLINE_OBJECT_METRICS 結構中。 IDWriteTextLayout 會呼叫此回呼函式,以取得内嵌物件的度量。

GetOverhangMetrics 方法。

HRESULT STDMETHODCALLTYPE InlineImage::GetOverhangMetrics(
    __out DWRITE_OVERHANG_METRICS* overhangs
    )
{
    overhangs->left      = 0;
    overhangs->top       = 0;
    overhangs->right     = 0;
    overhangs->bottom    = 0;
    return S_OK;
}

在此情況下,不需要加總,因此 GetOverhangMetrics 方法會傳回所有零。

GetBreakConditions 方法。

HRESULT STDMETHODCALLTYPE InlineImage::GetBreakConditions(
    __out DWRITE_BREAK_CONDITION* breakConditionBefore,
    __out DWRITE_BREAK_CONDITION* breakConditionAfter
    )
{
    *breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
    *breakConditionAfter  = DWRITE_BREAK_CONDITION_NEUTRAL;
    return S_OK;
}

步驟 4:建立 InlineImage 類別的實例,並將其新增至文字版面配置。

最後,在 CreateDeviceDependentResources 方法中,建立 InlineImage 類別的實例,並將它新增至文字版面配置。 因為它會保留 ID2D1RenderTarget的參考,這是裝置相依資源,而且會使用轉譯目標建立 ID2D1Bitmap ,因此 InlineImage 也是裝置相依的,而且如果重新建立轉譯目標,則必須重新建立。

// Create an InlineObject.
pInlineImage_ = new InlineImage(pRT_, pWICFactory_, L"img1.jpg");

DWRITE_TEXT_RANGE textRange = {14, 1};

pTextLayout_->SetInlineObject(pInlineImage_, textRange);

IDWriteTextLayout::SetInlineObject方法會採用文字範圍結構。 物件會套用至此處指定的範圍,且範圍中的任何文字都會隱藏。 如果文字範圍的長度是 0,將不會繪製物件。

在此範例中,有星號 (*) ,做為影像顯示位置的預留位置。

// The string to display.  The '*' character will be suppressed by our image.
wszText_ = L"Inline Object * Example";
cTextLength_ = wcslen(wszText_);

由於 InlineImage 類別相依于 ID2D1RenderTarget,因此當您釋放轉譯目標時,必須釋放它。

SafeRelease(&pInlineImage_);