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'interfaccia IDWriteTextLayout.
Il prodotto finale di questa esercitazione è un'applicazione che visualizza testo con un'immagine inline incorporata, come illustrato nella schermata seguente.
Questa esercitazione contiene le parti seguenti:
- Passaggio 1: Creare un layout di testo.
- Passaggio 2: Definire una classe derivata dall'interfaccia IDWriteInlineObject.
- Passaggio 3: Implementare la classe oggetto Inline.
- Passaggio 4: Creare un'istanza della classe InlineImage e aggiungerla al layout di testo.
Passaggio 1: Creare un layout di testo.
Per iniziare, è necessaria un'applicazione con un oggetto IDWriteTextLayout . Se è già disponibile 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:
Dichiarare un puntatore a un'interfaccia IDWriteTextLayout come membro della classe .
IDWriteTextLayout* pTextLayout_;
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. ); }
È quindi necessario modificare la chiamata al metodo ID2D1RenderTarget::D rawText in ID2D1RenderTarget::D rawTextLayout, 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 viene 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 su cui verrà eseguito il rendering dell'immagine inline. Questo viene archiviato per usarlo 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_.
Metodo Draw.
Il metodo Draw è un callback chiamato dall'oggetto IDWriteTextRenderer quando è necessario disegnare l'oggetto inline. Il layout di 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::D rawBitmap ; tuttavia, è possibile utilizzare qualsiasi metodo per il disegno.
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 misurazione 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 a ID2D1RenderTarget, ovvero una risorsa dipendente dal dispositivo e ID2D1Bitmap viene creato usando la destinazione di rendering, anche InlineImage è dipendente dal dispositivo e deve essere ricreato 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 da ID2D1RenderTarget, è necessario rilasciarla quando si rilascia la destinazione di rendering.
SafeRelease(&pInlineImage_);