Hinzufügen von Inlineobjekten zu einem Textlayout
Enthält ein kurzes Tutorial zum Hinzufügen von Inlineobjekten zu einer 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.
Um ein Textlayout hinzuzufügen, müssen Sie die folgenden Schritte ausführen:
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 verwenden zu können, müssen Sie diese Schnittstelle implementieren. Es verarbeitet das Zeichnen des Inlineobjekts sowie die Bereitstellung 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 werden, 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 methoden, die von der IUnknown-Schnittstelle geerbt werden, 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 das ID2D1RenderTarget, in dem das Inlineimage gerendert wird. Dies wird zur späteren Verwendung beim Zeichnen gespeichert.
Das Renderziel, IWICImagingFactory und der Dateinamen-URI werden 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 ruft diese Methode nicht direkt auf.
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 erfolgt das Zeichnen der Bitmap mithilfe der ID2D1RenderTarget::D rawBitmap-Methode . es kann jedoch eine beliebige 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 abzurufen.
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 Überhang 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 instance der InlineImage-Klasse, und fügen Sie sie dem Textlayout hinzu. Da es einen Verweis auf id2D1RenderTarget enthält, bei dem es sich um eine geräteabhängige Ressource handelt, und die ID2D1Bitmap mithilfe des Renderziels erstellt wird, ist das InlineImage auch 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 verwendet eine Textbereichsstruktur. 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 vom ID2D1RenderTarget abhängig ist, müssen Sie sie freigeben, wenn Sie das Renderziel freigeben.
SafeRelease(&pInlineImage_);