How to draw text with the default UI font in DirectWrite? - c++

The CreateTextFormat method expects a fontFamilyName parameter. How do I create an IDWriteTextFormat that uses the default UI font?

Please note, that all code here is done without any checks (too many methods here return HRESULT, would blow this example up!).
For acquiring the system wide font you should use this:
(This is from another stackoverflow question!)
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
HFONT hFont = CreateFontIndirect(&(ncm.lfMessageFont)); //
for paint now use this:
HDC hdc = BeginPaint(...); //Creates a device context
SelectObject(hdc, hFont);
//Your font is now set for the current device context
//do something
DeleteObject(hFont); //Don't forget to do this at the end!
A bit changed from this question!
This solution is really raw and in my opinion ugly.
Alternative solution do get the IDWriteFont (looks ugly but is fine):
//just the same as above except the hfont, instead use
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
IDWriteFactory *dwriteFactory_;
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&dwriteFactory_)
);
IDWriteGdiInterop* gdiInterop = NULL;
dwriteFactory_->GetGdiInterop(&gdiInterop);
IDWriteFont* sys_font = nullptr;
gdiInterop->CreateFontFromLOGFONT(&ncm.lfMessageFont, &sys_font); //Now we have it!
//The text format can now be aquired like this
//We need the font family of our font
IDWriteFontFamily* family = nullptr;
sys_font->GetFontFamily(&family);
//Now we have to get the "localized" name of our family
IDWriteLocalizedStrings* font_family_name = nullptr;
family->GetFamilyNames(&font_family_name);
UINT32 index = 0;
UINT32 length = 0;
BOOL exists = false;
font_family_name->FindLocaleName(L"en-us", &index, &exists);
font_family_name->GetStringLength(index, &length);
wchar_t* name = new wchar_t[length + 1];
font_family_name->GetString(index, name, length + 1);
wprintf(L"%s\n", name);
//Some user defined stuff
DWRITE_FONT_WEIGHT font_weight = DWRITE_FONT_WEIGHT_BLACK;
DWRITE_FONT_STYLE font_style = DWRITE_FONT_STYLE_ITALIC;
DWRITE_FONT_STRETCH font_stretch = DWRITE_FONT_STRETCH_EXPANDED;
IDWriteTextFormat* text_format = nullptr;
dwriteFactory_->CreateTextFormat(name, nullptr, font_weight, font_style, font_stretch, 10.0, L"en-us", &text_format);
Even without checks, code runs on my computer without any problems and gives me the same result as the first solution (Windows 10, Font family name is Segoe UI).
Sources:
General Microsoft DirectWrite API documentation
CreateIndirectFont Documentation
How to enumerate font families, Microsoft documentation

Related

Visual Studio C++ How do I stop images from being scaled wrong when I maximize window D3D11 and D2D1

So I'm trying to iron out kinks of how I'm rendering images with direct stuff. Right now it works as a DXGISwapchain with D3D11 and I make a ID2D1RenderTarget which I draw to using bitmaps. My issue is when I hit the maximize button on the window my images are off, or at least the ones using data from the window grabbed with GetClientRect (the others seem... close enough but probably still off and I want to be able to use the client space to scale and draw things later as well). I have a D2D1::RectF set with the top left at 0.0f and the bottom right as the window's height and width grabbed via GetClientRect (along with a few others just for additional fooling around). Looked around and it seems like I need to call recreate the ID2D1RenderTarget and/or resize the buffers. Calling a function to recreate the ID2D1RenderTarget before making objects which contain the bitmaps and the functions which draw them did not help with the issue at all. I tried resizing the buffers but I keep getting errors, first set were regarding parameters, but before fixing that I realized I needed to release the objects, but now my it seems since I have made the objects with ComPtr it seems how it deals with deleting them is having issues. client.h is calling an exception: "Access violation executing location " with the unsigned long InternalRelease() function. This occurs with the function to adjust the buffers and target. So right now I'm lost as to what to do in order to get the desired effect. Only other things to note is the ID3D11RenderTargetView I made is used to clear to a color since for I had errors with calling Clear(D2D1::ColorF(D2D1::ColorF::White)); on my ID2D1RenderTarget. I don't care if the solution to this resizes the ID3D11RenderTargetView unless it will improve speed for the program or prevent some sort of unforeseen issue elsewhere since I don't intend to use it aside for that. If I could call clear on the ID2D1RenderTarget and no longer need the ID311RenderTargetView and keep the swapchain while resolving the issue that would work too. Also I intend to work out fullscreen mode next so a method that works with that would also be very much desired. I'm also open to take any other advice here, while it's not polished and just in a form to get things working first, I probably will miss things even when tidying up. Anyway here's the code:
Here's the Graphics class where I make the swapchain, buffers and rendertargets and such and the function in which I try and reset the buffers. Side note I followed some tutorials on my way here to get me to understand enough of the direct stuff to get here and get to the point where I'm looking into to stuff on Microsoft and understanding it somewhat to solve problems I have. Anyway one of them went through making some exceptions and that is what stuff like Graphic_Throw_Failure() are for. (though I did it wrong or the errors are doing something and I can't see the pop up window half the time, sometimes it stops in the exception code but I can still read the message)
//not in the cpp file but just how some variables exist for clarity.
private:
Microsoft::WRL::ComPtr<ID3D11Device> pDevice = nullptr;
Microsoft::WRL::ComPtr < IDXGISwapChain> pSwapChain = nullptr;
Microsoft::WRL::ComPtr < ID3D11DeviceContext> pContext = nullptr;
Microsoft::WRL::ComPtr < ID3D11RenderTargetView> pRTarget = nullptr;
ID2D1RenderTarget* p2dRenderTarget = nullptr;
ID2D1Factory* p2DFactory = nullptr;
Graphics::Graphics(HWND hwnd) {
DXGI_SWAP_CHAIN_DESC swapchainDesc = {};
ZeroMemory(&swapchainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
swapchainDesc.Windowed = true;
swapchainDesc.BufferCount = 1;
swapchainDesc.BufferDesc.Height = 0;
swapchainDesc.BufferDesc.Width = 0;
swapchainDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapchainDesc.SampleDesc.Count = 1;
swapchainDesc.SampleDesc.Quality = 0;
swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapchainDesc.BufferDesc.RefreshRate.Numerator = 1;
swapchainDesc.BufferDesc.RefreshRate.Denominator = 60;
swapchainDesc.OutputWindow = hwnd;
HRESULT hre;
Graphic_Throw_Failure(D3D11CreateDeviceAndSwapChain(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
levels,
4,
D3D11_SDK_VERSION,
&swapchainDesc,
&pSwapChain,
&pDevice,
nullptr,
&pContext
));
//3Dbuffer setup
wrl::ComPtr<ID3D11Resource> p3dbuffer = nullptr;
Graphic_Throw_Failure(pSwapChain->GetBuffer(0, __uuidof(ID3D11Resource), &p3dbuffer));
Graphic_Throw_Failure(pDevice->CreateRenderTargetView(p3dbuffer.Get(), nullptr, &pRTarget));
//2D buffer Setup
IDXGISurface* pBackBuffer = nullptr;
Graphic_Throw_Failure(pSwapChain->GetBuffer(0,IID_PPV_ARGS(&pBackBuffer)));
//makes 2d Factory
Graphic_Throw_Failure(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &p2DFactory));
//sets up DXGI buffer for 2d
FLOAT dpi;
dpi = GetDpiForWindow(hwnd);
//p2DFactory->GetDesktopDpi(&dpiX, &dpiY);
D2D1_RENDER_TARGET_PROPERTIES p2dRTprops =
D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
dpi,
dpi
);
Graphic_Throw_Failure(p2DFactory->CreateDxgiSurfaceRenderTarget(
pBackBuffer, &p2dRTprops, &p2dRenderTarget
));
if(pBackBuffer!=nullptr)
pBackBuffer->Release();
}
//the adjusting function I failed to make. could also be missing somethings I need to clear before
//calling ResizeBuffers
void Graphics::adjustRenderTargets(HWND hwnd) {
HRESULT hre;
pContext->ClearState();
p2dRenderTarget->Release();
pRTarget->Release();
//3Dbuffer setup
wrl::ComPtr<ID3D11Resource> p3dbuffer = nullptr;
Graphic_Throw_Failure(pSwapChain->GetBuffer(0, __uuidof(ID3D11Resource), &p3dbuffer));
Graphic_Throw_Failure(pDevice->CreateRenderTargetView(p3dbuffer.Get(), nullptr, &pRTarget));
//2D buffer Setup
IDXGISurface* pBackBuffer = nullptr;
Graphic_Throw_Failure(pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)));
//makes 2d Factory
Graphic_Throw_Failure(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &p2DFactory));
//sets up DXGI buffer for 2d
FLOAT dpi;
dpi = GetDpiForWindow(hwnd);
//p2DFactory->GetDesktopDpi(&dpiX, &dpiY);
D2D1_RENDER_TARGET_PROPERTIES p2dRTprops =
D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
dpi,
dpi
);
Graphic_Throw_Failure(p2DFactory->CreateDxgiSurfaceRenderTarget(
pBackBuffer, &p2dRTprops, &p2dRenderTarget
));
if (pBackBuffer != nullptr)
pBackBuffer->Release();
};
//and the destructor in case there is something still wrong there though
//currently not giving me issues since I set the objects in it to nullptr after releasing it.
//didn't work for the ComPtr.
Graphics::~Graphics() {
if (p2DFactory != nullptr) {
p2DFactory->Release();
}
if (p2dRenderTarget != nullptr) {
p2dRenderTarget->Release();
}
}
This is the class which holds the bitmaps and deals with them and drawing them. Once again I made some exceptions for this class
//some variables in the header file
ID2D1Bitmap* Bittmap=nullptr;
Graphics* GFX;
Sprites::Sprites(const wchar_t* filename, Graphics* gfx) {
HRESULT hre;
GFX = gfx;
//makes WIC Factory
IWICImagingFactory* WICfactory = NULL;
Sprite_Throw_Failure(CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory,
(LPVOID*)&WICfactory
));
//Makes the Decoder
IWICBitmapDecoder* WICdecode = NULL;
Sprite_Throw_Failure(WICfactory->CreateDecoderFromFilename(
filename,
NULL,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
&WICdecode
));
//Read the frame (should be only one so read the image)
IWICBitmapFrameDecode* WICframe = NULL;
Sprite_Throw_Failure(WICdecode->GetFrame(0, &WICframe));
//Format converter
IWICFormatConverter* WICconverter = NULL;
Sprite_Throw_Failure(WICfactory->CreateFormatConverter(&WICconverter));
//makes the converter set up to create a 32bpp BGRA bitmap
Sprite_Throw_Failure(WICconverter->Initialize(
WICframe,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.0,
WICBitmapPaletteTypeCustom
));
//makes the bitmap
Sprite_Throw_Failure(GFX->Get2DRenderTarget()->CreateBitmapFromWicBitmap(
WICconverter,
NULL,
&Bittmap
));
if (WICfactory) WICfactory->Release();
if (WICdecode) WICdecode->Release();
if (WICconverter)WICconverter->Release();
if (WICframe)WICframe->Release();
}
//draws the sprites
void Sprites::Draw(D2D1_RECT_F location) {
HRESULT hre;
GFX->Get2DRenderTarget()->BeginDraw();
GFX->Get2DRenderTarget()->DrawBitmap(
Bittmap,
location, //destination rect
1.0f, //opacity
D2D1_BITMAP_INTERPOLATION_MODE::D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
D2D1::RectF(
1980.0f, 2850.0f, 3000.0f,
3600.0f) //source rect
);
Sprite_Throw_Failure(GFX->Get2DRenderTarget()-> EndDraw());
}
Sprites::~Sprites() {
//bitmapsheet.clear();
if (Bittmap != nullptr) {
Bittmap->Release();
}
}
This is the class which the main loop is handled wnd is the windowclass I made which makes and manages the window. I use it here to get the graphics object the window uses which has all the direct stuff. here are some variables that appear, forgive the name bob.
//in header file
private:
Window wnd;
//in cpp file
Sprites* testsprite;
Sprites* testsprite2;
D2D1_RECT_F bob;
within the function that calls over and over for the duration of the program at the part where I render:
//inefficient constant adjusting of rendering just so I can quickly assess
//the change and make sure it works so when I implement how I intend the windows to be scaled it
//will already be done
wnd.GFX().adjustRenderTargets(wnd.getwindowhandle());
//clearing the ID3D11RenderTargetView to a color
wnd.GFX().ClearBuffer(0.3f, 0.5f, 1.0f);
//drawing calls
const wchar_t* filename = L"\environmentsketches 02.png";
//this creates a Sprites object getGraphix returns the a pointer to the graphics object
//only really used to get the ID2D1RenderTarget but I may use it for other things, will
//remove later if not needed and just pass the render target unless issues arise.
testsprite = new Sprites(filename, wnd.GFX().getGraphix());
bob = D2D1::RectF(
0.0f, 0.0f, wnd.getwindowWidth(),
wnd.getwindowHeight());
//This draws the bitmap of a predetermined portion of the image but uses bob to
// to determine where to draw the bitmap
testsprite->Draw(bob);
bob = D2D1::RectF(
0.0f, 0.0f, wnd.getwindowWidth()/(16.0f/9.0f),
wnd.getwindowHeight());
testsprite->Draw(bob);
filename= L"\envrioment sketch march 1.png";
bob = D2D1::RectF(
100.0f, 100.0f, 600.f,
300.f);
testsprite2 = new Sprites(filename, wnd.GFX().getGraphix());
testsprite2->Draw(bob);
//EndFrame just calls present on the IDXGISwapChain
wnd.GFX().EndFrame();
testsprite->~Sprites();
testsprite2->~Sprites();
If you read through this thank you and thank you for any advice you have to offer.
Don't code late or tired. So first thing I realized is one when posting the code here I forgot to include the call for SwapChain->ResizeBuffers(0,width,height, DXGI_FORMAT_UNKNOWN, 0)) which is where the mistake was. I changed my pointers from smart to regular to manage their release manually for this, but the issue was more so that the last parameter wasn't 0, it was D3D11_CREATE_DEVICE_BGRA_SUPPORT instead of a proper swapchain flag and was reading as another (I believe DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY) which I couldn't use because of how I made my swapchain resulting in an error and it just not running.
In all the solution to my problem was to release my render targets (I already released by buffers after I made the targets), resize the buffers and then remake the ID2D1RenderTarget. Just don't put a wrong flag in, (or make sure the call is in when posting it for feedback to the mistake might be caught by others.)

HRESULT 0x8876086c D3DERR_INVALIDCALL with DirectX9 pretty much following the documentation example

So i wanna draw an overlay over another window, but im getting no real runtime error the visual Studio debugging tools tell me that the result of
HRESULT res = object->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWND, D3DCREATE_HARDWARE_VERTEXPROCESSING, &params, NULL, &device);
is 0x8876086c. So here are the snippets of my code that are important and lead to this error(D3DERR_INVALIDCALL), which leads to the device being a nullpointer, which means i can't do anything with it.
I couldn't really figure out what led to this as i pretty much followed the documentation
int Paint::init(HWND hWND) {
if (FAILED(Direct3DCreate9Ex(D3D_SDK_VERSION, &object))) {
exit(1);
}
ZeroMemory(&params, sizeof(params));
params.BackBufferWidth = width;
params.BackBufferHeight = height;
params.Windowed = true;
params.hDeviceWindow = hWND;
params.MultiSampleQuality = D3DMULTISAMPLE_NONE;
params.BackBufferFormat = D3DFMT_A8R8G8B8;
params.EnableAutoDepthStencil = TRUE;
params.AutoDepthStencilFormat = D3DFMT_D16;
HRESULT res = object->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWND, D3DCREATE_HARDWARE_VERTEXPROCESSING, &params, NULL, &device);
and in the header file:
class Paint {
private:
IDirect3D9Ex* object = NULL;
IDirect3DDevice9Ex* device = NULL;
DWORD behaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
D3DPRESENT_PARAMETERS params;
ID3DXFont* font = 0;
HWND TargetHWND;
int width, height;
int init(HWND(hWND));
}
D3DPRESENT_PARAMETERS params = {};
// Use Win32 BOOL "TRUE" instead of C++ "true"
params.Windowed = TRUE;
params.hDeviceWindow = m_window;
// params.BackBufferWidth, BackBufferHeight are ignored for Windowed = TRUE
// For Windowed = TRUE, use params.BackBufferFormat = D3DFMT_UNKNOWN, which is zero.
// For params.BackBufferCount zero is assumed to be 1, but best practice
// would be to set it
params.BackBufferCount = 1;
// You used D3DMULTISAMPLE_NONE for the MultiSampleQuality instead of MultiSampleType.
// It's all zero anyhow.
params.EnableAutoDepthStencil = TRUE;
params.AutoDepthStencilFormat = D3DFMT_D16;
// --->>> This is the actual bug: there is no valid SwapEffect that has a value of zero <<<---
params.SwapEffect = D3DSWAPEFFECT_DISCARD;
You are making the assumption that the Direct3D9 device supports D3DCREATE_HARDWARE_VERTEXPROCESSING, but you haven't validated it actually supports it. That said, D3DCREATE_SOFTWARE_VERTEXPROCESSING has known performance issues on Windows 10 so you should probably just require HW anyhow.
You should not be using legacy Direct3D9 or Direct3D9Ex for new projects. It's mostly emulated on newer versions of Windows, has lots of strange behaviors, and is almost 20 years old at this point. There's no support for the Direct3D 9 debug device on Windows 8.x or Windows 10. You should consider Direct3D 11 as a much better starting place for developers new to DirectX.

Win32 Use a resource font inside the application

I have an application, and I imported some fonts into the resource.
Now, I want to use those resourced fonts inside the application, without installing them to the computer that runs it.
The way I want to use the font resources is that I want to set a label's font to the resourced font by sending WM_SETFONT message to it.
Generally, if the font is already installed on the computer I would use the following code:
HDC hdc = GetDC(hwnd);
//here hwnd is the handle to the window.
const TCHAR* fontName = TEXT("/* THE FONT NAME */");
//this is where I'd enter the font name, but it only works when the font is already installed on the computer.
const long nFontSize = NFONTSIZE(7);
//this is where I set the font size.
LOGFONT logFont = {0};
logFont.lfHeight = -MulDiv(nFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
logFont.lfWeight = FW_SEMIBOLD;
_tcscpy_s(logFont.lfFaceName, fontName);
HFONT font = CreateFontIndirect(&logFont); //get the font handle
as soon as I get the HFONT handle, it's no difficulty to send the WM_SETFONT message to the label with:
SendMessage(hwnd, WM_SETFONT, (WPARAM)font, static_cast<LPARAM>(MAKELONG(TRUE, 0)));
//here hwnd is the handle of the static label.
But now, I don't want to set the font by this way because this only works when the specified font is already installed on the computer. I have MY OWN font file with the .ttf format imported as resource. I want to set the label's font to THIS .ttf font.
Assuming you have a token IDF_MYFONT defined for the resource ID, then you can embed your font in the executable with a line like this in your .rc (or .rc2) script:
IDF_MYFONT BINARY "..\\MyFont.ttf" // Or whatever the path to your font file is.
You can load and lock the font resource using code like the following:
HANDLE hMyFont = INVALID_HANDLE_VALUE; // Here, we will (hopefully) get our font handle
HINSTANCE hInstance = ::GetModuleHandle(nullptr); // Or could even be a DLL's HINSTANCE
HRSRC hFntRes = FindResource(hInstance, MAKEINTRESOURCE(IDF_MYFONT), L"BINARY");
if (hFntRes) { // If we have found the resource ...
HGLOBAL hFntMem = LoadResource(hInstance, hFntRes); // Load it
if (hFntMem != nullptr) {
void* FntData = LockResource(hFntMem); // Lock it into accessible memory
DWORD nFonts = 0, len = SizeofResource(hInstance, ares);
hMyFont = AddFontMemResourceEx(FntData, len, nullptr, &nFonts); // Fake install font!
}
}
Then, when you've finished with the font, you can release it from memory like this:
RemoveFontMemResourceEx(hMyFont);
I've included some checks on the return values for the system calls, but you can add others. And you will need to be able to handle the cases where any of these fail (e.g. providing a default font).
While the font is loaded/locked in memory, you can use it as though it were installed on the system: for example, using its name in a LOGFONT structure:
LOGFONT MyLogFont = { -8, 0, 0, 0, 400, FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
VARIABLE_PITCH | FF_SWISS, L"MyFontName" };
Feel free to ask for further clarification and/or explanation.
Funny thing, I was just dealing with this problem yesterday night...
To solve it, the values passed into FindResourceW must match the resource type:
const auto resourceHandle{ FindResourceW(nullptr, MAKEINTRESOURCE(IDR_FONT1), RT_FONT) };
const auto resourceLoad{ LoadResource(nullptr, resourceHandle) };
const auto pResource{ LockResource(resourceLoad) };
//do something with the resource pointer here...
FreeResource(resourceLoad);
This should give you a pointer to the resource, and you can then extract the font by creating a new file and write to it using WriteFile. To get the size of the resource, use SizeofResource.
Or you can create the font by passing in the resource pointer into AddFontMemResourceEx.

What is the correct way to use CFontDialog common dialog?

I recently change the relationship between the window extent and the view port of an MFC application, and, since then, every time I change my application font size, the selected font become supper gigantic even if I chose the smallest of all font size.
(EDIT: I noticed that CFontDialog::GetSize() return a size that is ten times the size chosen in the dialog. Is that a usual behaviour ? If not what could make the dialogue return such a value? Although I am not sure, but it seem this multiplication of size seem to be my problem. How do I get CFontDialog::GetSize() to return the actual selected size, if that indeed is the problem?)
What am I doing wrong? What is the correct way to use CFontDialog ?
Shown next is a snippet of the font changing code:
CClientDC dc(pView);
pView->OnPrepareDC(&dc)
pLastFont = pLastText->GetFont();
oldColor = pLastText->GetColor();
LOGFONT logFont = (LOGFONT) (*pLastFont);
CFontDialog fontDialog(&logFont);
CSize szPrevSize;
//Some missing codes
MyFigure *pMyFigure;
if(dynamic_cast<MyTextFigure*>(pMyFigure) != NULL)
{
if(dynamic_cast<MyTextBoxFigure*>(pLastText) != NULL)
{
MyTextBoxFigure *pTextBox = dynamic_cast<MyTextBoxFigure*>(pLastText);
szPrevSize = pTextBox->GetTextSize();
}
}
else if(dynamic_cast<MyTableFigure*>(pMyFigure) != NULL)
{
MyTableFigure *pTableFigure = dynamic_cast<MyTableFigure*>(pMyFigure);
TCell *pCell = (TCell *)*pTableFigure;
szPrevSize = pCell->GetTextSize();
}
fontDialog.m_cf.rgbColors = (COLORREF) oldColor;
if (fontDialog.DoModal() == IDOK)
{
fontDialog.GetCurrentFont(&logFont);
MyFont newFont = (MyFont) logFont;
MyColor newColor = (MyColor) fontDialog.GetColor();
pMyFigure->SetFont(newFont, &dc);
pMyFigure->SetColor(newColor);
}
Please note that MyFont is a wrapper around the LOGFONT structure and has an operator that enable cast to LOGFONT. Also note that MyFigure class has a MyFont data member that is set by the member function SetFont.
The following code shows how the relationship between the view port and the window extent was set.
void CDisplayView::OnInitialUpdate()
{
CRect rcClient;
GetClientRect(&rcClient);
CClientDC dc(this);
dc.SetMapMode(MM_ISOTROPIC);
CSize szWindow(m_pAppDoc->GetZoomRatio() * SCALE_RATIO * dc.GetDeviceCaps(HORZRES),m_pAppDoc->GetZoomRatio() * SCALE_RATIO * dc.GetDeviceCaps(VERTRES));
CSize szViewport(dc.GetDeviceCaps(HORZRES),dc.GetDeviceCaps(VERTRES));
dc.SetWindowExt(szWindow);
dc.SetViewportExt(szViewport);
dc.DPtoLP(&rcClient);
//And so on
}
ZoomRatio is 1, an SCALE_RATIO is 1.2
The assignment operator is:
MyFont& MyFont::operator=(const MyFont& font)
{
if (this != &font)
{
m_logFont = font.m_logFont;
}
return *this;
}
P.S.
The summary of the code is:
LOGFONT OldlogFont ;
LOGFONT newLogFont;
COLLORREF OldColorref;
COLORREF newColorref;
CFontDialog fontDialog(&OldlogFont);
fontDialog.m_cf.rgbColors = oldColorref;
if (fontDialog.DoModal() == IDOK)
{
fontDialog.GetCurrentFont(&OldlogFont);
newLogFont = OldlogFont;
newColorref = fontDialog.GetColor();
}
The focus here is the LOGFONT structure.
The user selected value is gotten with old logfont then assigned to new logfont.
For debugging purposes, I retrieved the size of the selected font and was shocked to find that it is ten times the size that I selected after lunching the CFontDialog for use in my application i.e
LOGFONT OldlogFont ;
LOGFONT newLogFont;
COLLORREF OldColorref;
COLORREF newColorref;
CFontDialog fontDialog(&OldlogFont);
fontDialog.m_cf.rgbColors = oldColorref;
if (fontDialog.DoModal() == IDOK)
{
fontDialog.GetCurrentFont(&OldlogFont);
newLogFont = OldlogFont;
newColorref = fontDialog.GetColor();
int iFontSize = fontDialog.GetSize();
//when I selected a font size of 10 from the dialog, what I get here in my code is 100.
}
This is how I do it:
void CExportSettingsDlg::OnBtnSelectFont()
{
CClientDC dc(this);
m_plfCurrentFont->lfHeight =
-MulDiv(m_plfCurrentFont->lfHeight, dc.GetDeviceCaps(LOGPIXELSY), 72);
CMyFontDialog dlgFont( m_plfCurrentFont );
dlgFont.m_cf.Flags |= CF_NOSCRIPTSEL;
dlgFont.m_cf.rgbColors = m_crCurrentFontColour;
if( dlgFont.DoModal() == IDOK)
{
m_plfCurrentFont->lfHeight = -(dlgFont.GetSize()/10);
// We must retrieve the font colour into the correct variable.
// The logfont is already correct because we passed in the
// logfont pointer into the font dialogue.
switch( m_iFontType )
{
case FONT_TITLE:
m_sSettings.crTitleFontColour = dlgFont.GetColor();
break;
case FONT_HEADING:
m_sSettings.crHeadingFontColour = dlgFont.GetColor();
break;
case FONT_GENERAL:
m_sSettings.crGeneralFontColour = dlgFont.GetColor();
break;
case FONT_EVENT:
m_sSettings.crEventFontColour = dlgFont.GetColor();
break;
case FONT_NOTES:
m_sSettings.crNotesFontColour = dlgFont.GetColor();
break;
}
// Update display
SetSampleFont();
// Html Preview
UpdatePreviewHtml();
}
}
If it does not answer your question I will remove the answer. Notice how I set the font size at the start of the code.
If you read up on the MSDN for GetSize it states:
The font's size, in tenths of a point

Debugging font linking

I have a problem with a program we're developing. It is written using MFC but does not use unicode. We have made a translation into simplified chinese. So far this is our first and only localization. We have moved all strings to resources to make them translateable. Everything seems to work fine on our computers (both Win7 and XP) but for some customers computers running windows XP we get problems:
On those computers all translated strings work except for those we enter into tree cotrols (CTreeCtrl is used directly). I'm not sure if it is the tree control or the text we enter that causes the problem, but the font seems to not get substituted in those controls. My guess is that maybe it does not get substituted because some of the strings also contain latin characters. Still, that kind of substitution seems to work in other places of the program and not all strings entered into the tree contain those characters either.
So my first question is: Is there a way to get to know what is happening inside the control? We can have remote access to the computers where the problem is happening but running a debugger on them may be a little tricky. What kind of tools can be used to diagnose this problem?
One possible solution that crossed my mind was to subclass the tree controls to get more control over the actual text drawing, maybe using the approach from http://blogs.msdn.com/b/oldnewthing/archive/2004/07/16/185261.aspx to get around the problem. Could this be effective or would it just be an awful lot of work for nothing?
Thanks a lot!
Ok, it seems on their system there was either a bug in the tree control implementation or they had some systemwide addin that hooked into the drawing in some way. What I ended up doing was to do custom drawing through notifications in the tree view and then using font linking to resolve the fonts. I dont think font linking would be necessary, though, because I think most of the time it ended up doing just TextOutW() with the preselected font anyway.
Here is a somewhat simplified code sample of what I ended up with:
void MyDlg::OnCustomDrawTreeItem( NMHDR* pNMHDR, LRESULT* pResult )
{
if (!mpFontLink)
{
*pResult = CDRF_DODEFAULT;
return;
}
LPNMTVCUSTOMDRAW pCustomdraw = (LPNMTVCUSTOMDRAW) pNMHDR;
switch(pCustomdraw->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
{
// Ask to do custom draw
*pResult = CDRF_NOTIFYITEMDRAW;
break;
}
case CDDS_ITEMPREPAINT:
{
// Ask for post paint notification
*pResult = CDRF_NOTIFYPOSTPAINT;
break;
}
case CDDS_ITEMPOSTPAINT:
{
// Get the rect of only the item, not the tree stuff
RECT rcItem;
m_pageTree.GetItemRect((HTREEITEM) pCustomdraw->nmcd.dwItemSpec, &rcItem, TRUE);
// Erase the item background in case the previous string drawn was wider
HDC hDC = pCustomdraw->nmcd.hdc;
FillRect( hDC, &rcItem, (HBRUSH) GetClassLongPtr(m_pageTree.GetSafeHwnd(), GCLP_HBRBACKGROUND));
pageStruct *pS = (pageStruct*) pCustomdraw->nmcd.lItemlParam;
DWORD dwFontCodepages = 0, dwStrCodepages = 0;
HFONT hOriginalFont = (HFONT)GetCurrentObject(hDC, OBJ_FONT);
HRESULT hr = mpFontLink->GetFontCodePages( hDC, hOriginalFont, &dwFontCodepages);
OML_CStringW tData = pS->csCaption.GetBuffer();
// Set up position etc
DWORD dwAlignOrig = GetTextAlign(hDC);
if (!(dwAlignOrig & TA_UPDATECP)) {
SetTextAlign(hDC, dwAlignOrig | TA_UPDATECP);
}
POINT ptOrig;
MoveToEx(hDC, 2 + rcItem.left, 1 + rcItem.top, &ptOrig);
SetTextColor( hDC, pCustomdraw->clrText );
SetBkColor( hDC, pCustomdraw->clrTextBk );
// Loop over the parts of the text
TuInt32 nIndex = 1;
while (nIndex <= tData.GetLength())
{
long nActualChars = 0;
wchar_t *pStr = (wchar_t*)tData.BaseGetItemP( nIndex );
TuInt32 nChars = 1 + tData.GetLength() - nIndex;
hr = mpFontLink->GetStrCodePages(pStr, nChars,
dwFontCodepages, &dwStrCodepages, &nActualChars);
if (dwStrCodepages & dwFontCodepages)
{
// We end up here almost every time, that is why TextOutW would probably be enough.
// This part is supported by the original font (or the GDI can help us switch automatically)
TextOutW(hDC, 0, 0, pStr, nActualChars);
}
else
{
// We need to link
HFONT hLinked;
if (FAILED(hr = mpFontLink->MapFont(hDC, dwStrCodepages, 0, &hLinked)))
{
// Fail: Output the rest without linking
TextOutW( hDC, 0, 0, pStr, nChars );
break;
}
// Output with linked font
SelectObject(hDC, hLinked);
TextOutW( hDC, 0, 0, pStr, nActualChars);
SelectObject(hDC, hOriginalFont);
mpFontLink->ReleaseFont( hOriginalFont );
}
nIndex += nActualChars;
}
OML_LOG_1( "_END:");
// Reset alignment mode
if (!(dwAlignOrig & TA_UPDATECP)) {
SetTextAlign(hDC, dwAlignOrig);
MoveToEx(hDC, ptOrig.x, ptOrig.y, NULL);
}
*pResult = CDRF_SKIPDEFAULT;
}
default:
{
*pResult = CDRF_DODEFAULT;
break;
}
}
} /* OnCustomDrawTreeItem */