Cómo agregar objetos insertados a un diseño de texto
Proporciona un breve tutorial sobre cómo agregar objetos insertados a una aplicación DirectWrite que muestra texto mediante la interfaz IDWriteTextLayout.
El producto final de este tutorial es una aplicación que muestra texto con una imagen insertada insertada en él, como se muestra en la siguiente captura de pantalla.
Este tutorial contiene las siguientes partes:
- Paso 1: Crear un diseño de texto.
- Paso 2: Definir una clase derivada de la interfaz IDWriteInlineObject.
- Paso 3: Implementar la clase de objeto insertada.
- Paso 4: Crear una instancia de la clase InlineImage y Agregarla al diseño de texto.
Paso 1: Crear un diseño de texto.
Para empezar, necesitará una aplicación con un objeto IDWriteTextLayout . Si ya tiene una aplicación que muestra texto con un diseño de texto, vaya al paso 2.
Para agregar un diseño de texto, debe hacer lo siguiente:
Declare un puntero a una interfaz IDWriteTextLayout como miembro de la clase .
IDWriteTextLayout* pTextLayout_;
Al final del método CreateDeviceIndependentResources, cree un objeto de interfaz IDWriteTextLayout llamando al método 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. ); }
A continuación, debe cambiar la llamada al método ID2D1RenderTarget::D rawText a ID2D1RenderTarget::D rawTextLayout, como se muestra en el código siguiente.
pRT_->DrawTextLayout( origin, pTextLayout_, pBlackBrush_ );
Paso 2: Definir una clase derivada de la interfaz IDWriteInlineObject.
La interfaz IDWriteInlineObject proporciona compatibilidad con objetos insertados en DirectWrite. Para usar objetos insertados, debe implementar esta interfaz. Controla el dibujo del objeto insertado, así como proporciona métricas y otra información al representador.
Cree un nuevo archivo de encabezado y declare una interfaz denominada InlineImage, derivada de IDWriteInlineObject.
Además de QueryInterface, AddRef y Release heredados de IUnknown, esta clase debe tener los métodos siguientes:
Paso 3: Implementar la clase de objeto insertada.
Cree un nuevo archivo de C++, denominado InlineImage.cpp, para la implementación de la clase. Además del método LoadBitmapFromFile y los métodos heredados de la interfaz IUnknown, la clase InlineImage se compone de los métodos siguientes.
The Constructor.
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_
);
}
El primer argumento del constructor es id2D1RenderTarget en el que se representará la imagen insertada. Esto se almacena para su uso más adelante al dibujar.
El destino de representación, IWICImagingFactory y el URI de nombre de archivo se pasan al método LoadBitmapFromFile que carga el mapa de bits y almacena el tamaño del mapa de bits (ancho y alto) en la variable miembro rect_.
El método Draw.
El método Draw es una devolución de llamada a la que llama el objeto IDWriteTextRenderer cuando es necesario dibujar el objeto insertado. El diseño de texto no llama directamente a este método.
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;
}
En este caso, el dibujo del mapa de bits se realiza mediante el método ID2D1RenderTarget::D rawBitmap ; sin embargo, se puede usar cualquier método para dibujar.
Método 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;
}
Para el método GetMetrics , almacene el ancho, el alto y la línea base en una estructura de DWRITE_INLINE_OBJECT_METRICS . IDWriteTextLayout llama a esta función de devolución de llamada para obtener la medida del objeto insertado.
Método 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;
}
En este caso, no es necesario ningún voladizo, por lo que el método GetOverhangMetrics devuelve todos los ceros.
Método 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;
}
Paso 4: Crear una instancia de la clase InlineImage y Agregarla al diseño de texto.
Por último, en el método CreateDeviceDependentResources, cree una instancia de la clase InlineImage y agréguela al diseño de texto. Dado que contiene una referencia a ID2D1RenderTarget, que es un recurso dependiente del dispositivo y id2D1Bitmap se crea mediante el destino de representación, InlineImage también depende del dispositivo y se debe volver a crear si se vuelve a crear el destino de representación.
// Create an InlineObject.
pInlineImage_ = new InlineImage(pRT_, pWICFactory_, L"img1.jpg");
DWRITE_TEXT_RANGE textRange = {14, 1};
pTextLayout_->SetInlineObject(pInlineImage_, textRange);
El método IDWriteTextLayout::SetInlineObject toma una estructura de intervalo de texto. El objeto se aplica al intervalo especificado aquí y se suprimirá cualquier texto del intervalo. Si la longitud del intervalo de texto es 0, el objeto no se dibujará.
En este ejemplo, hay un asterisco (*) como un marcador de posición en la posición donde se mostrará la imagen.
// The string to display. The '*' character will be suppressed by our image.
wszText_ = L"Inline Object * Example";
cTextLength_ = wcslen(wszText_);
Puesto que la clase InlineImage depende de ID2D1RenderTarget, debe liberarla al liberar el destino de representación.
SafeRelease(&pInlineImage_);