textmetrics.widthIncludingTrailingWhitespace computation is very imprecise at huge layoutWidth values for text alignment set to DWRITE_TEXT_ALIGNMENT_CENTERED. For example, we compute for a text width equal to 1586.44, and enter a value of 5E10 for layoutWidth.
This gives us for widthIncludingTrailingWhitespace a value of 2048.
A more detailed research suggests that widthIncludingTrailingWhitespace most probably is computed as TextRect.right - TextRect.left, and this subtraction operation is prone to accumulating errors, because datatypes of a diminuend and a subtracted
value are float, not double.
A huge layoutWidth value is used in a common technique for measuring text width, which includes a statement for creating text layout like this:
pDWriteFactory_->CreateTextLayout(
wszText_,
cTextLength_,
pTextFormat_,
FLOAT_MAX,
1.0f,
&pTextLayout_
)
A developer must be careful not to assign accidentally a text alignment value other than LEADING (left) before text metrics measurement operation.
Is it worth to correct DirectWrite API to improve the widthIncludingTrailingWhitespace computation?
Code:
// DWriteConsoleApp.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#define HR(x) { HRESULT nValue = (x); if (FAILED(nValue)) return nValue;}
int PrintTextmetrics(DWRITE_TEXT_METRICS textmetrics)
{
printf_s(" left: %g; top: %g\n height: %g; width: %g\n widthIncludingTrailingWhitespace: %g\n layoutWidth: %g; layoutHeight: %g\n lineCount: %d;
maxBidiReorderDepth %d\n\n",
textmetrics.left, textmetrics.top,
textmetrics.height, textmetrics.width, textmetrics.widthIncludingTrailingWhitespace,
textmetrics.layoutWidth, textmetrics.layoutHeight,
textmetrics.lineCount, textmetrics.maxBidiReorderingDepth);
return 0;
}
int main()
{
IDWriteFactory* pDWriteFactory_ = nullptr;
IDWriteTextFormat* pTextFormat_ = nullptr;
IDWriteTextLayout* pTextLayout_ = nullptr;
IDWriteTextLayout1* pTextLayout1_ = nullptr;
const wchar_t* wszText_;
UINT32 cTextLength_;
float layoutWidth = 5E10f;
float layoutHeight = 200.f;
int nScanf = 0;
HRESULT hr = S_OK;
do
{
printf_s("Layout Width ('-1' exits program, '0' defaults layoutWidth to 5E10):");
nScanf = scanf_s("%f", &layoutWidth);
if (layoutWidth < 0) return 0;
if (layoutWidth == 0)
layoutWidth = 5E10f;
HR(DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&pDWriteFactory_)
));
HR(pDWriteFactory_->CreateTextFormat(
L"Gabriola",
NULL,
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
64.0f,
L"en-us",
&pTextFormat_
));
wszText_ = L"Hello World using DirectWrite! And so on, to show us how text wraps in this app. ";
cTextLength_ = (UINT32)wcslen(wszText_);
HR(pDWriteFactory_->CreateTextLayout(
wszText_,
cTextLength_,
pTextFormat_,
layoutWidth,
layoutHeight,
&pTextLayout_
));
HR(pTextLayout_->QueryInterface(__uuidof(IDWriteTextLayout1), reinterpret_cast<void**>(&pTextLayout1_)));
DWRITE_TEXT_RANGE textRange = { 0, cTextLength_ };
HR(pTextLayout_->SetFontSize(64.0f, textRange));
HR(pTextLayout_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING)); // LEFT
DWRITE_TEXT_METRICS textmetrics;
HR(pTextLayout_->GetMetrics(&textmetrics));
printf_s("LEADING (Left) ALIGNED:\n");
PrintTextmetrics(textmetrics);
HR(pTextLayout_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); // CENTER
HR(pTextLayout_->GetMetrics(&textmetrics));
printf_s("CENTERED (Center) ALIGNED:\n");
PrintTextmetrics(textmetrics);
HR(pTextLayout_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING)); // RIGHT
HR(pTextLayout_->GetMetrics(&textmetrics));
printf_s("TRAILING (Right) ALIGNED:\n");
PrintTextmetrics(textmetrics);
HR(pTextLayout_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_JUSTIFIED)); // JUSTIFIED
HR(pTextLayout_->GetMetrics(&textmetrics));
printf_s("JUSTIFIEDSPACE ALIGNED:\n");
PrintTextmetrics(textmetrics);
} while (layoutWidth >= 0);
return 0;
}