2025-11-28 00:35:46 +09:00

879 lines
27 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
//
// Contents: Main user interface window.
//
//----------------------------------------------------------------------------
#include "Common.h"
#include "DrawingEffect.h"
#include "RenderTarget.h"
#include "EditableLayout.h"
#include "InlineImage.h"
#include "TextEditor.h"
#include "PadWrite.h"
#include "resource.h"
static void FailApplication(const wchar_t* message, int functionResult);
const static wchar_t g_sampleText[] =
L"\tDirectWrite SDK sample\r\n"
L"\n"
L"Feel free to play around with the formatting options to see just some of what DWrite is capable of:\n\n"
L"Glyph rendering, Complex script shaping, Script analysis, Bidi ordering (\x202E" L"abc" L"\x202C), Line breaking, Font fallback, "
L"Font enumeration, ClearType rendering, Bold/Italic/Underline/Strikethrough/Narrow/Light, OpenType styles, Inline objects \xFFFC\xFFFC, "
L"Trimming, Selection hit-testing...\r\n"
L"\r\n"
L"Mixed scripts: 한글 الْعَرَبيّة 中文 日本語 ภาษาไทย\r\n"
L"CJK characters beyond BMP - 𠂢𠂤𠌫𝟙𝟚𝟛\r\n"
L"Localized forms - Ş Ș vs Ş Ș; ۴۶ vs ۴۶\r\n"
L"Incremental tabs - 1 2 3"
;
////////////////////////////////////////
// Main entry.
int APIENTRY wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR commandLine,
int nCmdShow
)
{
// The Microsoft Security Development Lifecycle recommends that all
// applications include the following call to ensure that heap corruptions
// do not go unnoticed and therefore do not introduce opportunities
// for security exploits.
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
HRESULT hr = S_OK;
hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
MainWindow app;
app.AddRef(); // an implicit reference to the root window
hr = app.Initialize();
if (SUCCEEDED(hr))
hr = static_cast<HRESULT>(app.RunMessageLoop());
}
CoUninitialize();
if (FAILED(hr))
{
FailApplication(L"An unexpected error occured in the demo. Ending now...", hr);
}
return 0;
}
MainWindow::MainWindow()
: renderTargetType_(RenderTargetTypeD2D),
hwnd_(NULL),
dwriteFactory_(),
wicFactory_(),
d2dFactory_(),
renderTarget_(),
textEditor_(),
inlineObjectImages_()
{
// no heavyweight initialization in the constructor.
}
HRESULT MainWindow::Initialize()
{
// Initializes the factories and creates the main window,
// render target, and text editor.
HRESULT hr = S_OK;
//////////////////////////////
// Create the factories for D2D, DWrite, and WIC.
if (SUCCEEDED(hr))
{
hr = DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&dwriteFactory_)
);
}
// Create D2D factory
// Failure to create this factory is ok. We can live with GDI alone.
if (SUCCEEDED(hr))
{
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
&d2dFactory_
);
}
// Create WIC factory to load images.
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory,
(IID_PPV_ARGS(&wicFactory_))
);
}
//////////////////////////////
// Create the main window
if (SUCCEEDED(hr))
{
MainWindow::RegisterWindowClass();
TextEditor::RegisterWindowClass();
// create window (the hwnd is stored in the create event)
CreateWindow(
L"DirectWritePadDemo",
TEXT(APPLICATION_TITLE),
WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
800,
600,
NULL,
NULL,
HINST_THISCOMPONENT,
this
);
if (hwnd_ == NULL)
hr = HRESULT_FROM_WIN32(GetLastError());
}
//////////////////////////////
// Initialize the controls
if (SUCCEEDED(hr))
{
ShowWindow(hwnd_, SW_SHOWNORMAL);
UpdateWindow(hwnd_);
}
// Need a text format to base the layout on.
IDWriteTextFormat* textFormat = NULL;
if (SUCCEEDED(hr))
{
hr = dwriteFactory_->CreateTextFormat(
L"Arial",
NULL,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
16,
L"",
&textFormat
);
}
// Set initial text and assign to the text editor.
if (SUCCEEDED(hr))
{
hr = TextEditor::Create(
hwnd_,
g_sampleText,
textFormat,
dwriteFactory_,
&textEditor_
);
}
if (SUCCEEDED(hr))
{
hr = FormatSampleLayout(textEditor_->GetLayout());
}
// Create our target on behalf of text editor control
// and tell it to draw onto it.
if (SUCCEEDED(hr))
{
hr = CreateRenderTarget(textEditor_->GetHwnd(), RenderTargetTypeD2D);
textEditor_->SetRenderTarget(renderTarget_);
}
if (SUCCEEDED(hr))
{
// Size everything initially.
OnSize();
// Put focus on editor to begin typing.
SetFocus(textEditor_->GetHwnd());
}
SafeRelease(&textFormat);
return hr;
}
ATOM MainWindow::RegisterWindowClass()
{
// Registers window class.
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_DBLCLKS;
wcex.lpfnWndProc = &WindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = HINST_THISCOMPONENT;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszMenuName = MAKEINTRESOURCE(1);
wcex.lpszClassName = TEXT("DirectWritePadDemo");
wcex.hIconSm = NULL;
return RegisterClassEx(&wcex);
}
HRESULT MainWindow::CreateRenderTarget(HWND hwnd, RenderTargetType renderTargetType)
{
// Creates a render target, either a D2D surface or DirectWrite GDI DIB.
HRESULT hr = S_OK;
RenderTarget* renderTarget = NULL;
// Create the render target.
switch (renderTargetType)
{
case RenderTargetTypeD2D:
if (d2dFactory_ != NULL)
{
hr = RenderTargetD2D::Create(d2dFactory_, dwriteFactory_, hwnd, &renderTarget);
break;
}
// Fall through to DirectWrite if no D2D factory exists.
case RenderTargetTypeDW:
default:
renderTargetType = RenderTargetTypeDW;
hr = RenderTargetDW::Create(dwriteFactory_, hwnd, &renderTarget);
break;
}
// Set the new target.
if (SUCCEEDED(hr))
{
SafeSet(&renderTarget_, renderTarget);
renderTargetType_ = renderTargetType;
}
SafeRelease(&renderTarget);
return hr;
}
WPARAM MainWindow::RunMessageLoop()
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK MainWindow::WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// Relays messages for the main window to the internal class.
MainWindow* window = reinterpret_cast<MainWindow*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
switch (message)
{
case WM_NCCREATE:
{
// Associate the data structure with this window handle.
CREATESTRUCT* pcs = reinterpret_cast<CREATESTRUCT*>(lParam);
window = reinterpret_cast<MainWindow*>(pcs->lpCreateParams);
window->hwnd_ = hwnd;
window->AddRef(); // implicit reference via HWND
SetWindowLongPtr(hwnd, GWLP_USERDATA, PtrToUlong(window));
}
return DefWindowProc(hwnd, message, wParam, lParam);
case WM_COMMAND:
window->OnCommand(static_cast<UINT>(wParam));
break;
case WM_SIZE:
window->OnSize();
break;
case WM_DESTROY:
PostQuitMessage(0);
window->OnDestroy();
break;
case WM_NCDESTROY:
// Remove implicit reference via HWND.
// After this, the window and data structure no longer exist.
window->Release();
break;
case WM_SETFOCUS:
// Forward focus to the text editor.
if (window->textEditor_ != NULL)
SetFocus(window->textEditor_->GetHwnd());
break;
case WM_INITMENU:
// Menu about to be shown. Set check marks accordingly.
window->UpdateMenuToCaret();
break;
case WM_WINDOWPOSCHANGED:
// Window moved. Update ClearType settings if changed monitor.
if (window->renderTarget_ != NULL)
window->renderTarget_->UpdateMonitor();
return DefWindowProc(hwnd, message, wParam, lParam);
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
void MainWindow::OnCommand(UINT commandId)
{
// Handles menu commands.
IDWriteTextLayout* textLayout = textEditor_->GetLayout();
switch (commandId)
{
case CommandIdPaste:
textEditor_->PasteFromClipboard();
break;
case CommandIdCopy:
textEditor_->CopyToClipboard();
break;
case CommandIdDelete:
textEditor_->DeleteSelection();
break;
case CommandIdRenderD2D:
case CommandIdRenderDW:
CreateRenderTarget(textEditor_->GetHwnd(), RenderTargetType(commandId - CommandIdRenderFirst));
textEditor_->SetRenderTarget(renderTarget_);
break;
case CommandIdFont:
OnChooseFont();
break;
case CommandIdAlignLeading:
case CommandIdAlignHCenter:
case CommandIdAlignTrailing:
textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT(commandId - CommandIdAlignHFirst));
RedrawTextEditor();
break;
case CommandIdAlignTop:
case CommandIdAlignVCenter:
case CommandIdAlignBottom:
textLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT(commandId - CommandIdAlignVFirst));
RedrawTextEditor();
break;
case CommandIdLeftToRight:
textLayout->SetReadingDirection(DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
RedrawTextEditor();
break;
case CommandIdRightToLeft:
textLayout->SetReadingDirection(DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
RedrawTextEditor();
break;
case CommandIdWrap:
{
DWRITE_WORD_WRAPPING wordWrapping = textLayout->GetWordWrapping();
textLayout->SetWordWrapping((wordWrapping == DWRITE_WORD_WRAPPING_NO_WRAP)
? DWRITE_WORD_WRAPPING_WRAP
: DWRITE_WORD_WRAPPING_NO_WRAP
);
RedrawTextEditor();
}
break;
case CommandIdTrim:
{
// Retrieve existing trimming sign and settings
// and modify them according to button state.
IDWriteInlineObject* inlineObject = NULL;
DWRITE_TRIMMING trimming = { DWRITE_TRIMMING_GRANULARITY_NONE, 0, 0 };
textLayout->GetTrimming(&trimming, &inlineObject);
trimming.granularity = (trimming.granularity == DWRITE_TRIMMING_GRANULARITY_NONE)
? DWRITE_TRIMMING_GRANULARITY_CHARACTER
: DWRITE_TRIMMING_GRANULARITY_NONE;
textLayout->SetTrimming(&trimming, inlineObject);
SafeRelease(&inlineObject);
RedrawTextEditor();
}
break;
case CommandIdZoomIn:
textEditor_->SetScale(1.25f, 1.25f, true);
break;
case CommandIdZoomOut:
textEditor_->SetScale(1 / 1.25f, 1 / 1.25f, true);
break;
case CommandIdRotateCW:
textEditor_->SetAngle(90, true);
break;
case CommandIdRotateACW:
textEditor_->SetAngle(-90, true);
break;
case CommandIdResetView:
textEditor_->ResetView();
break;
case CommandIdSetInlineImage:
OnSetInlineImage();
break;
case CommandIdExit:
PostMessage(hwnd_, WM_CLOSE, 0, 0);
break;
}
return;
}
void MainWindow::OnSize()
{
// Updates the child edit control's size to fill the whole window.
if (textEditor_ == NULL)
return;
RECT clientRect = {};
GetClientRect(hwnd_, &clientRect);
SetWindowPos(
textEditor_->GetHwnd(),
NULL,
clientRect.left,
clientRect.top,
clientRect.right - clientRect.left,
clientRect.bottom - clientRect.top,
SWP_NOACTIVATE|SWP_NOZORDER
);
}
void MainWindow::OnDestroy()
{
SafeRelease(&textEditor_);
SafeRelease(&renderTarget_);
}
void MainWindow::RedrawTextEditor()
{
// Flags text editor to redraw itself after significant changes.
textEditor_->RefreshView();
}
void MainWindow::UpdateMenuToCaret()
{
// Updates the menu state according to the formatting
// at the current caret position.
IDWriteTextLayout* textLayout = textEditor_->GetLayout();
// Read layout-wide attributes from the layout.
DWRITE_TEXT_ALIGNMENT textAlignment = textLayout->GetTextAlignment();
DWRITE_WORD_WRAPPING wordWrapping = textLayout->GetWordWrapping();
DWRITE_READING_DIRECTION readingDirection = textLayout->GetReadingDirection();
DWRITE_TRIMMING trimming = { DWRITE_TRIMMING_GRANULARITY_NONE, 0, 0 };
IDWriteInlineObject* inlineObject = NULL;
textLayout->GetTrimming(&trimming, &inlineObject);
SafeRelease(&inlineObject); // We don't need the inline object.
HMENU hmenu = GetMenu(hwnd_);
CheckMenuItem(hmenu, CommandIdWrap, MF_BYCOMMAND | (wordWrapping != DWRITE_WORD_WRAPPING_NO_WRAP ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(hmenu, CommandIdTrim, MF_BYCOMMAND | (trimming.granularity != DWRITE_TRIMMING_GRANULARITY_NONE ? MF_CHECKED : MF_UNCHECKED));
CheckMenuRadioItem(hmenu, CommandIdAlignHFirst, CommandIdAlignHLast, CommandIdAlignHFirst + textAlignment, MF_BYCOMMAND);
CheckMenuRadioItem(hmenu, CommandIdLeftToRight, CommandIdRightToLeft, CommandIdLeftToRight + readingDirection, MF_BYCOMMAND);
CheckMenuRadioItem(hmenu, CommandIdRenderFirst, CommandIdRenderLast, CommandIdRenderFirst + renderTargetType_, MF_BYCOMMAND);
}
HRESULT MainWindow::OnChooseFont()
{
// Displays the font selection dialog,
// initializing it according to the current selection's format,
// and updating the current selection with the user's choice.
HRESULT hr = S_OK;
//////////////////////////////
// Read the caret format.
EditableLayout::CaretFormat& caretFormat = textEditor_->GetCaretFormat();
//////////////////////////////
// Initialize the LOGFONT from the caret format.
LOGFONT logFont = {};
logFont.lfHeight = -static_cast<LONG>(caretFormat.fontSize);
logFont.lfWidth = 0;
logFont.lfEscapement = 0;
logFont.lfOrientation = 0;
logFont.lfWeight = caretFormat.fontWeight;
logFont.lfItalic = (caretFormat.fontStyle > DWRITE_FONT_STYLE_NORMAL);
logFont.lfUnderline = static_cast<BYTE>(caretFormat.hasUnderline);
logFont.lfStrikeOut = static_cast<BYTE>(caretFormat.hasStrikethrough);
logFont.lfCharSet = DEFAULT_CHARSET;
logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logFont.lfQuality = DEFAULT_QUALITY;
logFont.lfPitchAndFamily = DEFAULT_PITCH;
StringCchCopy(logFont.lfFaceName, ARRAYSIZE(logFont.lfFaceName), caretFormat.fontFamilyName);
//////////////////////////////
// Initialize CHOOSEFONT for the dialog.
CHOOSEFONT chooseFont = {};
chooseFont.lStructSize = sizeof(chooseFont);
chooseFont.hwndOwner = hwnd_;
chooseFont.lpLogFont = &logFont;
chooseFont.iPointSize = 120; // Note that LOGFONT initialization takes precedence anyway.
chooseFont.rgbColors = DrawingEffect::GetColorRef(caretFormat.color);
chooseFont.Flags = CF_SCREENFONTS | CF_SCALABLEONLY | CF_NOVERTFONTS | CF_NOSCRIPTSEL | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT;
// We don't show vertical fonts because we don't do vertical layout,
// and don't show bitmap fonts because DirectWrite doesn't support them.
// Show the common font dialog box.
if (!ChooseFont(&chooseFont))
return hr; // user canceled.
//////////////////////////////
// Update the layout accordingly to what the user selected.
// Abort if the user didn't select a face name.
if (logFont.lfFaceName[0] == L'\0')
return hr;
IDWriteFont* font = NULL;
hr = CreateFontFromLOGFONT(logFont, &font);
if (SUCCEEDED(hr))
{
hr = GetFontFamilyName(font, caretFormat.fontFamilyName, ARRAYSIZE(caretFormat.fontFamilyName));
}
if (SUCCEEDED(hr))
{
caretFormat.hasUnderline = logFont.lfUnderline;
caretFormat.hasStrikethrough = logFont.lfStrikeOut;
caretFormat.fontWeight = font->GetWeight();
caretFormat.fontStretch = font->GetStretch();
caretFormat.fontStyle = font->GetStyle();
caretFormat.fontSize = floor(float(chooseFont.iPointSize * (96.0f / 720)));
caretFormat.color = DrawingEffect::GetBgra(chooseFont.rgbColors);
DWRITE_TEXT_RANGE textRange = textEditor_->GetSelectionRange();
if (textRange.length > 0)
{
IDWriteTextLayout* textLayout = textEditor_->GetLayout();
textLayout->SetUnderline(caretFormat.hasUnderline, textRange);
textLayout->SetStrikethrough(caretFormat.hasStrikethrough, textRange);
textLayout->SetFontWeight(caretFormat.fontWeight, textRange);
textLayout->SetFontStretch(caretFormat.fontStretch, textRange);
textLayout->SetFontStyle(caretFormat.fontStyle, textRange);
textLayout->SetFontSize(caretFormat.fontSize, textRange);
textLayout->SetFontFamilyName(caretFormat.fontFamilyName, textRange);
DrawingEffect* drawingEffect = SafeAcquire(new(std::nothrow) DrawingEffect(caretFormat.color));
textLayout->SetDrawingEffect(drawingEffect, textRange);
SafeRelease(&drawingEffect);
RedrawTextEditor();
}
}
SafeRelease(&font);
return hr;
}
HRESULT MainWindow::OnSetInlineImage()
{
// Displays a open dialog to choose the image to insert.
HRESULT hr = S_OK;
//////////////////////////////
// Initialize OPENFILENAME for the dialog.
wchar_t fileName[MAX_PATH];
fileName[0] = 0;
OPENFILENAME chooseFile = {};
chooseFile.lStructSize = sizeof(chooseFile);
chooseFile.hwndOwner = hwnd_;
chooseFile.hInstance = GetModuleHandle(NULL);
chooseFile.lpstrFilter = L"Supported images\0" L"*.png;*.jpg;*.jpeg;*.tif;*.tiff;*.bmp;*.gif\0" L"All files\0" L"(*)\0";
chooseFile.lpstrFile = &fileName[0];
chooseFile.nMaxFile = ARRAYSIZE(fileName);
chooseFile.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
// Get filename.
if (!GetOpenFileName(&chooseFile))
return hr ;
//////////////////////////////
// Create an inline object, using WIC to load the image.
IWICBitmapSource* bitmap = NULL;
hr = InlineImage::LoadImageFromFile(
fileName,
wicFactory_,
&bitmap
);
// Get the range of text to be replaced with an inline image.
// If no text is selected, insert the Unicode object replacement
// character as an anchor.
if (SUCCEEDED(hr))
{
DWRITE_TEXT_RANGE textRange = textEditor_->GetSelectionRange();
if (textRange.length <= 0)
{
textRange.length = 1;
textEditor_->InsertText(L"\xFFFC");
textEditor_->SetSelection(TextEditor::SetSelectionModeAbsoluteLeading, textRange.startPosition, false, false);
textEditor_->SetSelection(TextEditor::SetSelectionModeAbsoluteLeading, textRange.startPosition + 1, true, false);
}
IDWriteTextLayout* textLayout = textEditor_->GetLayout();
IDWriteInlineObject* inlineObject = SafeAcquire(new(std::nothrow) InlineImage(bitmap));
textLayout->SetInlineObject(inlineObject, textRange);
SafeRelease(&inlineObject);
}
SafeRelease(&bitmap);
RedrawTextEditor();
return hr;
}
STDMETHODIMP MainWindow::CreateFontFromLOGFONT(
const LOGFONT& logFont,
OUT IDWriteFont** font
)
{
*font = NULL;
HRESULT hr = S_OK;
// Conversion to and from LOGFONT uses the IDWriteGdiInterop interface.
IDWriteGdiInterop* gdiInterop = NULL;
hr = dwriteFactory_->GetGdiInterop(&gdiInterop);
// Find the font object that best matches the specified LOGFONT.
if (SUCCEEDED(hr))
{
hr = gdiInterop->CreateFontFromLOGFONT(&logFont, font);
}
SafeRelease(&gdiInterop);
return hr;
}
STDMETHODIMP MainWindow::GetFontFamilyName(
IDWriteFont* font,
OUT wchar_t* fontFamilyName,
UINT32 fontFamilyNameLength
)
{
HRESULT hr = S_OK;
// Get the font family to which this font belongs.
IDWriteFontFamily* fontFamily = NULL;
hr = font->GetFontFamily(&fontFamily);
// Get the family names. This returns an object that encapsulates one or
// more names with the same meaning but in different languages.
IDWriteLocalizedStrings* localizedFamilyNames = NULL;
if (SUCCEEDED(hr))
{
hr = fontFamily->GetFamilyNames(&localizedFamilyNames);
}
// Get the family name at index zero. If we were going to display the name
// we'd want to try to find one that matched the use locale, but for purposes
// of setting the current font, any language will do.
if (SUCCEEDED(hr))
{
hr = localizedFamilyNames->GetString(0, &fontFamilyName[0], fontFamilyNameLength);
}
SafeRelease(&localizedFamilyNames);
SafeRelease(&fontFamily);
return S_OK;
}
HRESULT MainWindow::FormatSampleLayout(IDWriteTextLayout* textLayout)
{
// Formats the initial sample text with styles, drawing effects, and
// typographic features.
HRESULT hr = S_OK;
// Load images for inline objects.
if (SUCCEEDED(hr))
{
hr = InlineImage::LoadImageFromResource(L"InlineObjects", L"Image", wicFactory_, &inlineObjectImages_);
}
// Set default color of black on entire range.
DrawingEffect* drawingEffect = SafeAcquire(new(std::nothrow) DrawingEffect(0xFF000000));
if (SUCCEEDED(hr))
{
textLayout->SetDrawingEffect(drawingEffect, MakeDWriteTextRange(0));
}
// Set initial trimming sign, but leave it disabled (granularity is none).
IDWriteInlineObject* inlineObject = NULL;
if (SUCCEEDED(hr))
{
hr = dwriteFactory_->CreateEllipsisTrimmingSign(
textLayout,
&inlineObject
);
}
if (SUCCEEDED(hr))
{
const static DWRITE_TRIMMING trimming = { DWRITE_TRIMMING_GRANULARITY_NONE, 0, 0 };
textLayout->SetTrimming(&trimming, inlineObject);
}
if (SUCCEEDED(hr))
{
textLayout->SetReadingDirection(DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
textLayout->SetFontFamilyName(L"Segoe UI", MakeDWriteTextRange(0));
textLayout->SetFontSize(18, MakeDWriteTextRange(0));
// Apply a color to the title words.
{
DrawingEffect* drawingEffect1 = SafeAcquire(new(std::nothrow) DrawingEffect(0xFF1010D0));
DrawingEffect* drawingEffect2 = SafeAcquire(new(std::nothrow) DrawingEffect(0xFF10D010));
textLayout->SetDrawingEffect(drawingEffect1, MakeDWriteTextRange(0, 7));
textLayout->SetDrawingEffect(drawingEffect2, MakeDWriteTextRange (7, 5));
SafeRelease(&drawingEffect2);
SafeRelease(&drawingEffect1);
}
// Set title font style.
textLayout->SetFontSize(30, MakeDWriteTextRange(0, 25)); // first line of text
textLayout->SetFontSize(60, MakeDWriteTextRange(1, 11)); // DirectWrite
textLayout->SetFontStyle(DWRITE_FONT_STYLE_ITALIC, MakeDWriteTextRange(0, 25) );
textLayout->SetFontFamilyName(L"Gabriola", MakeDWriteTextRange(1, 11));
// Add fancy swashes.
{
IDWriteTypography* typoFeature = NULL;
dwriteFactory_->CreateTypography(&typoFeature);
DWRITE_FONT_FEATURE feature = {DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_7, 1};
typoFeature->AddFontFeature(feature);
textLayout->SetTypography(typoFeature, MakeDWriteTextRange(1,11));
SafeRelease(&typoFeature);
}
// Apply decorations on demonstrated features.
textLayout->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, MakeDWriteTextRange(277, 4));
textLayout->SetFontStyle(DWRITE_FONT_STYLE_ITALIC, MakeDWriteTextRange(282, 6) );
textLayout->SetUnderline(TRUE, MakeDWriteTextRange(289, 9));
textLayout->SetStrikethrough(TRUE, MakeDWriteTextRange(299, 13));
textLayout->SetFontFamilyName(L"Arial", MakeDWriteTextRange(313, 6));
textLayout->SetFontStretch(DWRITE_FONT_STRETCH_CONDENSED, MakeDWriteTextRange(313, 6));
textLayout->SetFontWeight(DWRITE_FONT_WEIGHT_LIGHT, MakeDWriteTextRange(320, 5));
// Add two inline objects.
InlineImage* image1 = SafeAcquire(new(std::nothrow) InlineImage(inlineObjectImages_, 0));
InlineImage* image2 = SafeAcquire(new(std::nothrow) InlineImage(inlineObjectImages_, 1));
textLayout->SetInlineObject(image1, MakeDWriteTextRange(359, 1));
textLayout->SetInlineObject(image2, MakeDWriteTextRange(360, 1));
SafeRelease(&image1);
SafeRelease(&image2);
// Localized S with comma below.
textLayout->SetFontFamilyName(L"Tahoma", MakeDWriteTextRange(507, 3));
textLayout->SetFontSize(16, MakeDWriteTextRange(507, 3));
textLayout->SetFontFamilyName(L"Tahoma", MakeDWriteTextRange(514, 3));
textLayout->SetFontSize(16, MakeDWriteTextRange(514, 3));
textLayout->SetLocaleName(L"ro-ro", MakeDWriteTextRange(514, 3));
// Localized forms of extended Arabic-Indic numbers 4 & 6, Pakistan Urdu.
textLayout->SetFontFamilyName(L"Tahoma", MakeDWriteTextRange(519, 2));
textLayout->SetFontFamilyName(L"Tahoma", MakeDWriteTextRange(525, 2));
textLayout->SetLocaleName(L"ur-PK", MakeDWriteTextRange(525, 2));
}
SafeRelease(&inlineObject);
SafeRelease(&drawingEffect);
return hr;
}
void FailApplication(const wchar_t* message, int functionResult)
{
// Displays an error message and quits the program.
wchar_t buffer[1000];
buffer[0] = '\0';
const wchar_t* format = L"%s\r\nError code = %X";
StringCchPrintf(buffer, ARRAYSIZE(buffer), format, message, functionResult);
MessageBox(NULL, buffer, TEXT(APPLICATION_TITLE), MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
ExitProcess(functionResult);
}