Condividi tramite


Come aggiungere oggetti inline a un layout di testo

Viene fornita una breve esercitazione sull'aggiunta di oggetti inline a un'applicazione DirectWrite che visualizza il testo usando l'interfacciaIDWriteTextLayout.

Il prodotto finale di questa esercitazione è un'applicazione che visualizza testo con un'immagine inline incorporata, come illustrato nella schermata seguente.

schermata di

Questa esercitazione contiene le parti seguenti:

Passaggio 1: Creare un layout di testo.

Per iniziare, è necessaria un'applicazione con un oggetto IDWriteTextLayout. Se si dispone già di un'applicazione che visualizza il testo con un layout di testo, passare al passaggio 2.

Per aggiungere un layout di testo, è necessario eseguire le operazioni seguenti:

  1. Dichiarare un puntatore all'interfaccia IDWriteTextLayout come membro della classe.

    IDWriteTextLayout* pTextLayout_;
    
    
  2. Alla fine del metodo CreateDeviceIndependentResources creare un oggetto interfaccia IDWriteTextLayout chiamando il metodo 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. È quindi necessario modificare la chiamata al metodo ID2D1RenderTarget::DrawText in ID2D1RenderTarget::DrawTextLayout, come illustrato nel codice seguente.

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

Passaggio 2: Definire una classe derivata dall'interfaccia IDWriteInlineObject.

Il supporto per gli oggetti inline in DirectWrite è fornito dall'interfaccia IDWriteInlineObject . Per usare oggetti inline, è necessario implementare questa interfaccia. Gestisce il disegno dell'oggetto inline, oltre a fornire metriche e altre informazioni al renderer.

Creare un nuovo file di intestazione e dichiarare un'interfaccia denominata InlineImage, derivata da IDWriteInlineObject.

Oltre a QueryInterface, AddRef e Release ereditato da IUnknown, questa classe deve avere i metodi seguenti:

Passaggio 3: Implementare la classe oggetto Inline.

Creare un nuovo file C++, denominato InlineImage.cpp, per l'implementazione della classe. Oltre al metodo LoadBitmapFromFile e ai metodi ereditati dall'interfaccia IUnknown, la classe InlineImage è costituita dai metodi seguenti.

Costruttore.

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

Il primo argomento del costruttore è l'ID2D1RenderTarget a cui verrà eseguito il rendering dell'immagine inline. Questa operazione viene archiviata per essere utilizzata in un secondo momento durante il disegno.

La destinazione di rendering, IWICImagingFactory e l'URI del nome file vengono tutti passati al metodo LoadBitmapFromFile che carica la bitmap e archivia le dimensioni della bitmap (larghezza e altezza) nella variabile membro rect_.

Il Metodo del Disegno.

Il metodoDraw è un callback chiamato dall'oggetto IDWriteTextRenderer quando è necessario disegnare l'oggetto inline. Il layout del testo non chiama direttamente questo metodo.

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

In questo caso, il disegno della bitmap viene eseguito usando il metodo ID2D1RenderTarget::DrawBitmap; tuttavia, è possibile utilizzare qualsiasi metodo per disegnare.

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

Per il metodo GetMetrics, archiviare la larghezza, l'altezza e la linea di base in una struttura DWRITE_INLINE_OBJECT_METRICS. IDWriteTextLayout chiama questa funzione di callback per ottenere la misura dell'oggetto inline.

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

In questo caso, non è necessario alcun overhang, quindi il metodo GetOverhangMetrics restituisce tutti gli zeri.

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

Passaggio 4: Creare un'istanza della classe InlineImage e aggiungerla al layout di testo.

Infine, nel metodo CreateDeviceDependentResources creare un'istanza della classe InlineImage e aggiungerla al layout di testo. Poiché contiene un riferimento al ID2D1RenderTarget, che è una risorsa dipendente dal dispositivo e la ID2D1Bitmap viene creata usando la destinazione di rendering, anche InlineImage è dipendente dal dispositivo e deve essere ricreata se la destinazione di rendering viene ricreata.

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

DWRITE_TEXT_RANGE textRange = {14, 1};

pTextLayout_->SetInlineObject(pInlineImage_, textRange);

Il metodo IDWriteTextLayout::SetInlineObject accetta una struttura dell'intervallo di testo. L'oggetto si applica all'intervallo specificato qui e qualsiasi testo nell'intervallo verrà eliminato. Se la lunghezza dell'intervallo di testo è 0, l'oggetto non verrà disegnato.

In questo esempio è presente un asterisco (*) come segnaposto nella posizione in cui verrà visualizzata l'immagine.

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

Poiché la classe InlineImage dipende dalla ID2D1RenderTarget, è necessario rilasciarla quando si rilascia la destinazione di rendering.

SafeRelease(&pInlineImage_);