Добавление встроенных объектов в текстовый макет
Краткое руководство по добавлению встроенных объектов в приложение DirectWrite, которое отображает текст с помощью интерфейса IDWriteTextLayout.
Конечным продуктом этого руководства является приложение, которое отображает текст со встроенным изображением, как показано на следующем снимке экрана.
Это руководство содержит следующие части:
- Шаг 1. Создание макета текста.
- Шаг 2. Определите класс, производный от интерфейса IDWriteInlineObject.
- Шаг 3. Реализация класса встроенных объектов.
- Шаг 4. Создайте экземпляр класса InlineImage и добавьте его в макет текста.
Шаг 1. Создание макета текста.
Для начала потребуется приложение с объектом IDWriteTextLayout . Если у вас уже есть приложение, отображающее текст с макетом текста, перейдите к шагу 2.
Чтобы добавить макет текста, необходимо выполнить следующие действия.
Объявите указатель на интерфейс IDWriteTextLayout в качестве члена класса .
IDWriteTextLayout* pTextLayout_;
В конце метода CreateDeviceIndependentResources создайте объект интерфейса IDWriteTextLayout , вызвав метод 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. ); }
Затем необходимо изменить вызов метода ID2D1RenderTarget::D rawText на ID2D1RenderTarget::D rawTextLayout, как показано в следующем коде.
pRT_->DrawTextLayout( origin, pTextLayout_, pBlackBrush_ );
Шаг 2. Определите класс, производный от интерфейса IDWriteInlineObject.
Поддержка встроенных объектов в DirectWrite обеспечивается интерфейсом IDWriteInlineObject. Чтобы использовать встроенные объекты, необходимо реализовать этот интерфейс. Он обрабатывает рисование встроенного объекта, а также предоставляет отрисовщику метрики и другие сведения.
Создайте новый файл заголовка и объявите интерфейс InlineImage, производный от IDWriteInlineObject.
Помимо QueryInterface, AddRef и Release, унаследованных от IUnknown, этот класс должен иметь следующие методы:
Шаг 3. Реализация класса встроенных объектов.
Создайте новый файл C++ с именем InlineImage.cpp для реализации класса. Помимо метода LoadBitmapFromFile и методов, унаследованных от интерфейса IUnknown, класс InlineImage состоит из следующих методов.
Конструктор.
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_
);
}
Первым аргументом конструктора является ID2D1RenderTarget, в который будет отображаться встроенное изображение. Он сохраняется для последующего использования при рисовании.
Цель отрисовки, IWICImagingFactory и URI имени файла передаются методу LoadBitmapFromFile, который загружает растровое изображение и сохраняет размер растрового изображения (ширину и высоту) в переменной-члене rect_.
Метод Draw.
Метод Draw — это обратный вызов, который вызывается объектом IDWriteTextRenderer , когда необходимо нарисовать встроенный объект. Макет текста не вызывает этот метод напрямую.
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;
}
В этом случае рисование растрового изображения выполняется с помощью метода ID2D1RenderTarget::D rawBitmap ; однако можно использовать любой метод рисования.
Метод 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;
}
Для метода GetMetrics сохраните ширину, высоту и базовый план в DWRITE_INLINE_OBJECT_METRICS структуре. IDWriteTextLayout вызывает эту функцию обратного вызова для получения измерения встроенного объекта.
Метод 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;
}
В этом случае не требуется навес, поэтому метод GetOverhangMetrics возвращает все нули.
Метод 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;
}
Шаг 4. Создайте экземпляр класса InlineImage и добавьте его в макет текста.
Наконец, в методе CreateDeviceDependentResources создайте экземпляр класса InlineImage и добавьте его в текстовый макет. Поскольку он содержит ссылку на ID2D1RenderTarget, который является ресурсом, зависящим от устройства, и ID2D1Bitmap создается с помощью целевого объекта отрисовки, InlineImage также зависит от устройства и должен быть повторно создан при повторном создании целевого объекта отрисовки.
// Create an InlineObject.
pInlineImage_ = new InlineImage(pRT_, pWICFactory_, L"img1.jpg");
DWRITE_TEXT_RANGE textRange = {14, 1};
pTextLayout_->SetInlineObject(pInlineImage_, textRange);
Метод IDWriteTextLayout::SetInlineObject принимает структуру текстового диапазона. Объект применяется к указанному здесь диапазону, и любой текст в диапазоне будет подавляться. Если длина текстового диапазона равна 0, объект не будет нарисован.
В этом примере в качестве заполнителя в позиции, где будет отображаться изображение, имеется звездочка (*).
// The string to display. The '*' character will be suppressed by our image.
wszText_ = L"Inline Object * Example";
cTextLength_ = wcslen(wszText_);
Так как класс InlineImage зависит от ID2D1RenderTarget, его необходимо освободить при освобождении целевого объекта отрисовки.
SafeRelease(&pInlineImage_);