How to render subscripts or superscripts using DirectWrite custom renderer?

나느 41 Reputation points
2023-07-25T07:30:19.41+00:00

Subscripts and superscripts rendering is not implemented in DirectWrite(at least I couldn't find it), so I've made my own render following this. Though it seems to be very illegal, I modified fontEmSize and glyphAdvances member of DWRITE_GLYPH_RUN in order to imitate the result of superscripts and subscripts. However, this only move glyphs leftside but the width of the region remains unchanged. Modified glyphAdvances doesn't fix this. How can these empty gaps be removed? Or is there a more proper way to render subscripts or superscripts?

Here's the result of below code, and please ignore the underline and strikethrough.

User's image

(The superscripts was rendered with modified font size and glyph advances, while the subscripts was rendered with modified font size only.)

Here's my code:

HRESULT __stdcall TextRenderer::DrawGlyphRun(void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, DWRITE_GLYPH_RUN const* glyphRun, DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, IUnknown* clientDrawingEffect) noexcept
{
	auto pDC = reinterpret_cast<ID2D1DeviceContext6*>(clientDrawingContext);
	auto effect = reinterpret_cast<ScriptEffect*>(clientDrawingEffect);
	auto glyph = (DWRITE_GLYPH_RUN*)(glyphRun); // seems to be illegal but what i can do lol
	auto offset = new DWRITE_GLYPH_OFFSET[glyph->glyphCount]; // this only change position of glyph, so not used
	for (int i = 0; i < glyph->glyphCount; i++) offset[i] = { 0, 0 };
	glyph->glyphOffsets = offset;
	IDWriteFontFace3* fontFace;
	glyphRun->fontFace->QueryInterface(__uuidof(IDWriteFontFace3), reinterpret_cast<void**>(&fontFace));
	DWRITE_FONT_METRICS1 metrics;
	fontFace->GetMetrics(&metrics);
	
	switch (effect->GetScriptType())
	{
	case SCRIPTTYPE_SUBSCRIPT:
		baselineOriginY -= metrics.subscriptPositionY / (float)metrics.subscriptSizeY * glyph->fontEmSize;
		glyph->fontEmSize = glyph->fontEmSize / 2;
		break;
	case SCRIPTTYPE_SUPERSCRIPT:
		baselineOriginY -= metrics.superscriptPositionY / (float)metrics.superscriptSizeY * glyph->fontEmSize;
		glyph->fontEmSize = glyph->fontEmSize / 2;
		for (int i = 0; i < glyph->glyphCount; i++)
		{
			((FLOAT*)(glyph->glyphAdvances))[i] = glyph->glyphAdvances[i] / 2; // seems to be illegal too
		}
		break;
	}
	pDC->DrawGlyphRun(D2D1::Point2F(baselineOriginX, baselineOriginY), glyph, effect->GetBrush(), measuringMode);
	delete[] offset;
	return S_OK;
}

Windows development Windows API - Win32
Developer technologies C++
0 comments No comments
{count} votes

Accepted answer
  1. Jeanine Zhang-MSFT 11,356 Reputation points Microsoft External Staff
    2023-07-26T02:08:54.29+00:00

    Hello,

    Welcome to Microsoft Q&A!

    As far as I'm concerned, you should call the IDWriteTextLayout::SetFontSize function to reduce the font size of what it would normally be. And call IDWriteTextLayout::SetDrawingEffect Method to set the text range of the custom drawing effect.

    For more details, you could refer to the thread: https://social.msdn.microsoft.com/Forums/en-US/53c6c9da-74f2-46b0-b08e-84a06bab4547/how-can-i-use-superscripts-with-directwrite?forum=vcgeneral

    Thank you.

    Jeanine


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.