Comment ajouter des objets inline à une disposition de texte

Fournit un court tutoriel sur l’ajout d’objets inline à une application DirectWrite qui affiche du texte à l’aide de l’interface IDWriteTextLayout.

Le produit final de ce tutoriel est une application qui affiche du texte avec une image incorporée, comme illustré dans la capture d’écran suivante.

capture d’écran de « exemple d’objet inline » avec une image incorporée

Ce didacticiel contient les parties suivantes :

Étape 1 : Créer une disposition de texte.

Pour commencer, vous aurez besoin d’une application avec un objet IDWriteTextLayout . Si vous avez déjà une application qui affiche du texte avec une disposition de texte, passez à l’étape 2.

Pour ajouter une disposition de texte, vous devez effectuer les opérations suivantes :

  1. Déclarez un pointeur vers une interface IDWriteTextLayout en tant que membre de la classe.

    IDWriteTextLayout* pTextLayout_;
    
    
  2. À la fin de la méthode CreateDeviceIndependentResources, créez un objet d’interface IDWriteTextLayout en appelant la méthode 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. Ensuite, vous devez remplacer l’appel à la méthode ID2D1RenderTarget::D rawText par ID2D1RenderTarget::D rawTextLayout, comme indiqué dans le code suivant.

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

Étape 2 : Définissez une classe dérivée de l’interface IDWriteInlineObject.

La prise en charge des objets inline dans DirectWrite est assurée par l’interface IDWriteInlineObject. Pour utiliser des objets inline, vous devez implémenter cette interface. Il gère le dessin de l’objet inline, ainsi que la fourniture de métriques et d’autres informations au convertisseur.

Créez un fichier d’en-tête et déclarez une interface appelée InlineImage, dérivée d’IDWriteInlineObject.

Outre QueryInterface, AddRef et Release héritées d’IUnknown, cette classe doit avoir les méthodes suivantes :

Étape 3 : Implémenter la classe d’objets inline.

Créez un fichier C++, nommé InlineImage.cpp, pour l’implémentation de classe. En plus de la méthode LoadBitmapFromFile et des méthodes héritées de l’interface IUnknown, la classe InlineImage est composée des méthodes suivantes.

Constructeur.

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

Le premier argument du constructeur est l’ID2D1RenderTarget vers lequel l’image inline sera rendue. Il est stocké pour une utilisation ultérieure lors du dessin.

La cible de rendu, IWICImagingFactory et l’URI du nom de fichier sont tous passés à la méthode LoadBitmapFromFile qui charge l’image bitmap et stocke la taille de bitmap (largeur et hauteur) dans la variable membre rect_.

Méthode Draw.

La méthode Draw est un rappel appelé par l’objet IDWriteTextRenderer lorsque l’objet inline doit être dessiné. La disposition de texte n’appelle pas cette méthode directement.

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

Dans ce cas, le dessin de la bitmap est effectué à l’aide de la méthode ID2D1RenderTarget::D rawBitmap ; toutefois, n’importe quelle méthode de dessin peut être utilisée.

Méthode 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;
}

Pour la méthode GetMetrics , stockez la largeur, la hauteur et la ligne de base dans une structure DWRITE_INLINE_OBJECT_METRICS . IDWriteTextLayout appelle cette fonction de rappel pour obtenir la mesure de l’objet inline.

Méthode 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;
}

Dans ce cas, aucun surplomb n’est nécessaire, de sorte que la méthode GetOverhangMetrics retourne tous les zéros.

Méthode 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;
}

Étape 4 : Créer une instance de la classe InlineImage et l’ajouter à la disposition de texte.

Enfin, dans la méthode CreateDeviceDependentResources, créez un instance de la classe InlineImage et ajoutez-le à la disposition de texte. Étant donné qu’il contient une référence à ID2D1RenderTarget, qui est une ressource dépendante de l’appareil, et que l’ID2D1Bitmap est créé à l’aide de la cible de rendu, InlineImage dépend également de l’appareil et doit être recréé si la cible de rendu est recréée.

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

DWRITE_TEXT_RANGE textRange = {14, 1};

pTextLayout_->SetInlineObject(pInlineImage_, textRange);

La méthode IDWriteTextLayout::SetInlineObject prend une structure de plage de texte. L’objet s’applique à la plage spécifiée ici, et tout texte de la plage est supprimé. Si la longueur de la plage de texte est 0, l’objet n’est pas dessiné.

Dans cet exemple, il existe un astérisque (*) en tant que support de place à la position où l’image sera affichée.

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

Étant donné que la classe InlineImage dépend de l’ID2D1RenderTarget, vous devez la libérer lorsque vous relâchez la cible de rendu.

SafeRelease(&pInlineImage_);