Hinzufügen von Inlineobjekten zu einem Textlayout
Enthält ein kurzes Tutorial zum Hinzufügen von Inlineobjekten zu DirectWrite Anwendung, die Text mithilfe der IDWriteTextLayout-Schnittstelle anzeigt.
Das Endprodukt dieses Tutorials ist eine Anwendung, die Text mit einem eingebetteten Inlinebild anzeigt, wie im folgenden Screenshot gezeigt.

Dieses Tutorial enthält die folgenden Teile:
- Schritt 1: Erstellen eines Textlayouts.
- Schritt 2: Definieren Sie eine Klasse, die von der IDWriteInlineObject-Schnittstelle abgeleitet ist.
- Schritt 3: Implementieren der Inlineobjektklasse.
- Schritt 4: Erstellen Sie eine Instanz der InlineImage-Klasse, und fügen Sie sie dem Textlayout hinzu.
Schritt 1: Erstellen eines Textlayouts.
Zunächst benötigen Sie eine Anwendung mit einem IDWriteTextLayout-Objekt. Wenn Sie bereits über eine Anwendung verfügen, die Text mit einem Textlayout anzeigt, fahren Sie mit Schritt 2 fort.
Gehen Sie wie folgt vor, um ein Textlayout hinzuzufügen:
Deklarieren Sie einen Zeiger auf eine IDWriteTextLayout-Schnittstelle als Member der -Klasse.
IDWriteTextLayout* pTextLayout_;Erstellen Sie am Ende der CreateDeviceIndependentResources-Methode ein IDWriteTextLayout-Schnittstellenobjekt, indem Sie die CreateTextLayout-Methode aufrufen.
// 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. ); }Anschließend müssen Sie den Aufruf der ID2D1RenderTarget::D rawText-Methode in ID2D1RenderTarget::D rawTextLayoutändern, wie im folgenden Code gezeigt.
pRT_->DrawTextLayout( origin, pTextLayout_, pBlackBrush_ );
Schritt 2: Definieren Sie eine Klasse, die von der IDWriteInlineObject-Schnittstelle abgeleitet ist.
Unterstützung für Inlineobjekte in DirectWrite wird von der IDWriteInlineObject-Schnittstelle bereitgestellt. Um Inlineobjekte zu verwenden, müssen Sie diese Schnittstelle implementieren. Er verarbeitet das Zeichnen des Inlineobjekts sowie das Bereitstellen von Metriken und anderen Informationen für den Renderer.
Erstellen Sie eine neue Headerdatei, und deklarieren Sie eine Schnittstelle namens InlineImage, die von IDWriteInlineObject abgeleitet ist.
Zusätzlich zu QueryInterface, AddRef und Release, die von IUnknown geerbt wurden, muss diese Klasse über die folgenden Methoden verfügen:
Schritt 3: Implementieren der Inlineobjektklasse.
Erstellen Sie eine neue C++-Datei mit dem Namen InlineImage.cpp für die Klassenimplementierung. Zusätzlich zur LoadBitmapFromFile-Methode und den von der IUnknown-Schnittstelle geerbten Methoden besteht die InlineImage-Klasse aus den folgenden Methoden.
Der Konstruktor.
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_
);
}
Das erste Argument des Konstruktors ist id2D1RenderTarget, in das das Inlinebild gerendert wird. Diese wird zur späteren Verwendung beim Zeichnen gespeichert.
Das Renderziel IWICImagingFactory und der Dateinamen-URI werden alle an die LoadBitmapFromFile-Methode übergeben, die die Bitmap lädt und die Bitmapgröße (Breite und Höhe) in der Rect-Membervariable _ speichert.
Die Draw-Methode.
Die Draw-Methode ist ein Rückruf, der vom IDWriteTextRenderer-Objekt aufgerufen wird, wenn das Inlineobjekt gezeichnet werden muss. Das Textlayout aufruft diese Methode nicht direkt.
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 diesem Fall wird die Bitmap mithilfe der ID2D1RenderTarget::D rawBitmap-Methode gezeichnet. allerdings kann jede Methode zum Zeichnen verwendet werden.
Die GetMetrics-Methode.
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;
}
Speichern Sie für die GetMetrics-Methode die Breite, Höhe und Baseline in einer DWRITE _ INLINE OBJECT _ _ METRICS-Struktur. IDWriteTextLayout ruft diese Rückruffunktion auf, um die Messung des Inlineobjekts zu erhalten.
Die GetOverhangMetrics-Methode.
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 diesem Fall ist kein Überhängen erforderlich, sodass die GetOverhangMetrics-Methode alle Nullen zurückgibt.
Die GetBreakConditions-Methode.
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;
}
Schritt 4: Erstellen Sie eine Instanz der InlineImage-Klasse, und fügen Sie sie dem Textlayout hinzu.
Erstellen Sie schließlich in der CreateDeviceDependentResources-Methode eine Instanz der InlineImage-Klasse, und fügen Sie sie dem Textlayout hinzu. Da sie einen Verweis auf id2D1RenderTargetenthält, bei der es sich um eine geräteabhängige Ressource handelt, und die ID2D1Bitmap mithilfe des Renderziels erstellt wird, ist das InlineImage ebenfalls geräteabhängig und muss neu erstellt werden, wenn das Renderziel neu erstellt wird.
// Create an InlineObject.
pInlineImage_ = new InlineImage(pRT_, pWICFactory_, L"img1.jpg");
DWRITE_TEXT_RANGE textRange = {14, 1};
pTextLayout_->SetInlineObject(pInlineImage_, textRange);
Die IDWriteTextLayout::SetInlineObject-Methode nimmt eine Textbereichsstruktur an. Das -Objekt gilt für den hier angegebenen Bereich, und jeder Text im Bereich wird unterdrückt. Wenn die Länge des Textbereichs 0 ist, wird das Objekt nicht gezeichnet.
In diesem Beispiel gibt es ein Sternchen ( ) als Platzhalter an der Position, an der * das Bild angezeigt wird.
// The string to display. The '*' character will be suppressed by our image.
wszText_ = L"Inline Object * Example";
cTextLength_ = wcslen(wszText_);
Da die InlineImage-Klasse von ID2D1RenderTargetabhängig ist, müssen Sie sie beim Veröffentlichen des Renderziels veröffentlichen.
SafeRelease(&pInlineImage_);