Compartilhar via


Como adicionar objetos embutidos a um layout de texto

Fornece um breve tutorial sobre como adicionar objetos embutidos a um aplicativo DirectWrite que exibe texto usando a interface IDWriteTextLayout.

O produto final deste tutorial é um aplicativo que exibe texto com uma imagem embutida inserida nele, conforme mostrado na captura de tela a seguir.

captura de tela de

Este tutorial contém as seguintes partes:

Etapa 1: criar um layout de texto.

Para começar, você precisará de um aplicativo com um objeto IDWriteTextLayout . Se você já tiver um aplicativo que exibe texto com um layout de texto, pule para a Etapa 2.

Para adicionar um layout de texto, você deve fazer o seguinte:

  1. Declare um ponteiro para uma interface IDWriteTextLayout como um membro da classe .

    IDWriteTextLayout* pTextLayout_;
    
    
  2. No final do método CreateDeviceIndependentResources, crie um objeto de interface IDWriteTextLayout chamando o método CreateTextLayout .

    // 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. Em seguida, você deve alterar a chamada para o método ID2D1RenderTarget::D rawText para ID2D1RenderTarget::D rawTextLayout, conforme mostrado no código a seguir.

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

Etapa 2: defina uma classe derivada da interface IDWriteInlineObject.

O suporte para objetos embutidos no DirectWrite é fornecido pela interface IDWriteInlineObject. Para usar objetos embutidos, você deve implementar essa interface. Ele manipula o desenho do objeto embutido, bem como fornece métricas e outras informações para o renderizador.

Crie um novo arquivo de cabeçalho e declare uma interface chamada InlineImage, derivada de IDWriteInlineObject.

Além de QueryInterface, AddRef e Release herdados de IUnknown, essa classe deve ter os seguintes métodos:

Etapa 3: implementar a classe de objeto embutido.

Crie um novo arquivo C++, chamado InlineImage.cpp, para a implementação da classe. Além do método LoadBitmapFromFile e dos métodos herdados da interface IUnknown, a classe InlineImage é composta pelos métodos a seguir.

O Construtor.

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

O primeiro argumento do construtor é o ID2D1RenderTarget para o qual a imagem embutida será renderizada. Isso é armazenado para uso posterior ao desenhar.

O destino de renderização, IWICImagingFactory e o uri filename são todos passados para o método LoadBitmapFromFile que carrega o bitmap e armazena o tamanho do bitmap (largura e altura) na variável de membro rect_.

O método Draw.

O método Draw é um retorno de chamada chamado pelo objeto IDWriteTextRenderer quando o objeto embutido precisa ser desenhado. O layout de texto não chama esse método diretamente.

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

Nesse caso, desenhar o bitmap é feito usando o método ID2D1RenderTarget::D rawBitmap ; no entanto, qualquer método para desenho pode ser usado.

O método 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;
}

Para o método GetMetrics , armazene a largura, a altura e a linha de base em uma estrutura DWRITE_INLINE_OBJECT_METRICS . IDWriteTextLayout chama essa função de retorno de chamada para obter a medida do objeto embutido.

O método 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;
}

Nesse caso, nenhuma saliência é necessária, portanto, o método GetOverhangMetrics retorna todos os zeros.

O método 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;
}

Etapa 4: criar uma instância da classe InlineImage e adicioná-la ao Layout de Texto.

Por fim, no método CreateDeviceDependentResources, crie uma instância da classe InlineImage e adicione-a ao layout de texto. Como ele contém uma referência ao ID2D1RenderTarget, que é um recurso dependente do dispositivo, e o ID2D1Bitmap é criado usando o destino de renderização, o InlineImage também depende do dispositivo e deve ser recriado se o destino de renderização for recriado.

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

DWRITE_TEXT_RANGE textRange = {14, 1};

pTextLayout_->SetInlineObject(pInlineImage_, textRange);

O método IDWriteTextLayout::SetInlineObject usa uma estrutura de intervalo de texto. O objeto se aplica ao intervalo especificado aqui e qualquer texto no intervalo será suprimido. Se o comprimento do intervalo de texto for 0, o objeto não será desenhado.

Neste exemplo, há um asterisco (*) como um espaço reservado na posição em que a imagem será exibida.

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

Como a classe InlineImage depende do ID2D1RenderTarget, você deve liberá-la quando liberar o destino de renderização.

SafeRelease(&pInlineImage_);