// This method takes an attributed string and outputs a GlyphLayout data // structure that contains the glyph number and location of each inidividual glyph void getGlyphLayout (const AttributedString& text, GlyphLayout& glyphLayout) { // For now we are creating the DirectWrite Factory, System Font Collection, // D2D Factory and GDI Render target every time we layout text. // This is inefficient and we may be loading and unloading libraries each layout. // These four things should be created once at application startup and be destroyed // when the application exits. I'm not sure where the best place to do this so // for now I will just use the inefficient method. IDWriteFactory* dwFactory = nullptr; HRESULT hr = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&dwFactory)); IDWriteFontCollection* dwFontCollection = nullptr; hr = dwFactory->GetSystemFontCollection (&dwFontCollection); // To add color to text, we need to create a D2D render target // Since we are not actually rendering to a D2D context we create a temporary GDI render target ID2D1Factory *d2dFactory = nullptr; hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2dFactory); D2D1_RENDER_TARGET_PROPERTIES d2dRTProp = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_SOFTWARE, D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), 0, 0, D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE, D2D1_FEATURE_LEVEL_DEFAULT ); ID2D1DCRenderTarget* d2dDCRT = nullptr; hr = d2dFactory->CreateDCRenderTarget (&d2dRTProp, &d2dDCRT); // Initially we set the paragraph up with a default font and then apply the attributed string ranges later Font defaultFont; const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (defaultFont, *dwFontCollection); // We should probably be detecting the locale instead of hard coding it to en-us String localeName("en-us"); // We multiply the font height by the size factor so we layout text at the correct size IDWriteTextFormat* dwTextFormat = nullptr; hr = dwFactory->CreateTextFormat ( defaultFont.getTypefaceName().toWideCharPointer(), dwFontCollection, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, defaultFont.getHeight() * defaultFontHeightToEmSizeFactor, localeName.toWideCharPointer(), &dwTextFormat ); // Paragraph Attributes // Set Paragraph Alignment if (text.getTextAlignment() == AttributedString::left) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_LEADING); if (text.getTextAlignment() == AttributedString::right) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_TRAILING); if (text.getTextAlignment() == AttributedString::center) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_CENTER); // DirectWrite cannot justify text, default to left alignment if (text.getTextAlignment() == AttributedString::justified) dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_LEADING); // Set Word Wrap if (text.getWordWrap() == AttributedString::none) dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_NO_WRAP); if (text.getWordWrap() == AttributedString::byWord) dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_WRAP); // DirectWrite does not support wrapping by character, default to wrapping by word if (text.getWordWrap() == AttributedString::byChar) dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_WRAP); // DirectWrite does not automatically set reading direction // This must be set correctly and manually when using RTL Scripts (Hebrew, Arabic) if (text.getReadingDirection() == AttributedString::rightToLeft) dwTextFormat->SetReadingDirection (DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); IDWriteTextLayout* dwTextLayout = nullptr; hr = dwFactory->CreateTextLayout ( text.getText().toWideCharPointer(), text.getText().length(), dwTextFormat, glyphLayout.getWidth(), glyphLayout.getHeight(), &dwTextLayout ); // Character Attributes int numCharacterAttributes = text.getCharAttributesSize(); for (int i = 0; i < numCharacterAttributes; ++i) { Attr* attr = text.getCharAttribute (i); // Character Range Error Checking if (attr->range.getStart() > text.getText().length()) continue; if (attr->range.getEnd() > text.getText().length()) attr->range.setEnd (text.getText().length()); if (attr->attribute == Attr::font) { AttrFont* attrFont = static_cast<AttrFont*>(attr); DWRITE_TEXT_RANGE dwRange; dwRange.startPosition = attrFont->range.getStart(); dwRange.length = attrFont->range.getLength(); dwTextLayout->SetFontFamilyName (attrFont->font.getTypefaceName().toWideCharPointer(), dwRange); // We multiply the font height by the size factor so we layout text at the correct size const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (attrFont->font, *dwFontCollection); dwTextLayout->SetFontSize (attrFont->font.getHeight() * fontHeightToEmSizeFactor, dwRange); } if (attr->attribute == Attr::foregroundColour) { AttrColour* attrColour = static_cast<AttrColour*>(attr); DWRITE_TEXT_RANGE dwRange; dwRange.startPosition = attrColour->range.getStart(); dwRange.length = attrColour->range.getLength(); ID2D1SolidColorBrush* d2dBrush = nullptr; d2dDCRT->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF(attrColour->colour.getFloatRed(), attrColour->colour.getFloatGreen(), attrColour->colour.getFloatBlue(), attrColour->colour.getFloatAlpha())), &d2dBrush); // We need to call SetDrawingEffect with a legimate brush to get DirectWrite to break text based on colours dwTextLayout->SetDrawingEffect (d2dBrush, dwRange); safeRelease (&d2dBrush); } } UINT32 actualLineCount = 0; hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount); // Preallocate GlyphLayout Line Array glyphLayout.setNumLines (actualLineCount); HeapBlock <DWRITE_LINE_METRICS> dwLineMetrics (actualLineCount); hr = dwTextLayout->GetLineMetrics (dwLineMetrics, actualLineCount, &actualLineCount); int location = 0; // Create GlyphLine structures for each line in the layout for (UINT32 i = 0; i < actualLineCount; ++i) { // Get string range Range<int> lineStringRange (location, (int) location + dwLineMetrics[i].length); location = dwLineMetrics[i].length; GlyphLine* glyphLine = new GlyphLine(); glyphLine->setStringRange (lineStringRange); glyphLayout.addGlyphLine (glyphLine); } // To copy glyph data from DirectWrite into our own data structures we must create our // own CustomTextRenderer. Instead of passing the draw method an actual graphics context, // we pass it the GlyphLayout object that needs to be filled with glyphs. CustomDirectWriteTextRenderer* textRenderer = nullptr; textRenderer = new CustomDirectWriteTextRenderer(); hr = dwTextLayout->Draw ( &glyphLayout, textRenderer, glyphLayout.getX(), glyphLayout.getY() ); safeRelease (&textRenderer); safeRelease (&dwTextLayout); safeRelease (&dwTextFormat); safeRelease (&d2dDCRT); safeRelease (&d2dFactory); safeRelease (&dwFontCollection); safeRelease (&dwFactory); }
touchmind::VISITOR_RESULT operator()(std::shared_ptr<touchmind::model::node::NodeModel> node) { HRESULT hr = S_OK; m_pXPSPrint->_PrintNode(node); if (FAILED(hr)) { LOG(SEVERITY_LEVEL_WARN) << L"_PrintNodeBody failed at node(" << node->GetId() << L", hr = " << hr; return touchmind::VISITOR_RESULT_STOP; } XpsDWriteTextRenderer* pTextRenderer = m_pXPSPrint->GetTextRenderer(); touchmind::view::node::NodeViewManager *pNodeViewManager = m_pXPSPrint->GetNodeViewManager(); control::DWriteEditControlManager *pEditControlManager = m_pXPSPrint->GetEditControlManager(); control::EDIT_CONTROL_INDEX editControlIndex = pNodeViewManager->GetEditControlIndexFromNodeId(node->GetId()); std::shared_ptr<control::DWriteEditControl> pEditControl = pEditControlManager->GetEditControl(editControlIndex); if (pEditControl != nullptr) { IDWriteTextLayout* pTextLayout = pEditControl->GetTextLayout(); if (pTextLayout != nullptr) { pTextLayout->Draw(nullptr, pTextRenderer, node->GetX() + m_pXPSPrint->GetConfiguration()->GetInsets().left, node->GetY() + m_pXPSPrint->GetConfiguration()->GetInsets().top); } else { LOG(SEVERITY_LEVEL_WARN) << L"TextLayout is null"; } } else { LOG(SEVERITY_LEVEL_WARN) << L"Could not found EditControl for Node(" << node->GetId() << L")"; } return touchmind::VISITOR_RESULT_CONTINUE; }
void DWriteContext::DrawText(HDC hdc, const WCHAR* text, int len, int x, int y, int w, int h, int cellWidth, COLORREF color) { HRESULT hr = S_OK; IDWriteBitmapRenderTarget *bmpRT = NULL; // Skip when any fonts are not set. if (mTextFormat == NULL) return; // Check possibility of zero divided error. if (cellWidth == 0 || mDpiScaleX == 0.0f || mDpiScaleY == 0.0f) return; if (SUCCEEDED(hr)) hr = mGdiInterop->CreateBitmapRenderTarget(hdc, w, h, &bmpRT); if (SUCCEEDED(hr)) { IDWriteTextLayout *textLayout = NULL; HDC memdc = bmpRT->GetMemoryDC(); BitBlt(memdc, 0, 0, w, h, hdc, x, y, SRCCOPY); hr = mDWriteFactory->CreateGdiCompatibleTextLayout( text, len, mTextFormat, PixelsToDipsX(w), PixelsToDipsY(h), mDpiScaleX, NULL, TRUE, &textLayout); if (SUCCEEDED(hr)) { DWRITE_TEXT_RANGE textRange = { 0, (UINT32)len }; textLayout->SetFontWeight(mFontWeight, textRange); textLayout->SetFontStyle(mFontStyle, textRange); } if (SUCCEEDED(hr)) { GdiTextRenderer *renderer = new GdiTextRenderer(bmpRT, mRenderingParams); GdiTextRendererContext data = { color, PixelsToDipsX(cellWidth), 0.0f }; textLayout->Draw(&data, renderer, 0, 0); SafeRelease(&renderer); } BitBlt(hdc, x, y, w, h, memdc, 0, 0, SRCCOPY); SafeRelease(&textLayout); } SafeRelease(&bmpRT); }
void DrawNormal(const Chat &chat, int vpos) { int x = static_cast<int>(rcWnd_.right - (float)(chat.width + rcWnd_.right) * (vpos - chat.vpos) / VPOS_LEN); int y = static_cast<int>(((float)yPitch_ * CNJIni::GetSettings()->commentLineMargin) * (float)chat.line); IDWriteTextLayout *pLayout; pDWriteFactory_->CreateTextLayout(chat.text.c_str(), chat.text.length(), pTextFormat_, 1920, 1200, &pLayout); D2D1_POINT_2F pt = {static_cast<FLOAT>(x) / dpiScaleX_, static_cast<FLOAT>(y) / dpiScaleY_}; ID2D1SolidColorBrush *pColor; pRT_->CreateSolidColorBrush( D2D1::ColorF(bgr2rgb(chat.color)), &pColor ); renderer_->SetColorBrush(pColor); pLayout->Draw(NULL, pTextRenderer_, pt.x, pt.y); pColor->Release(); pLayout->Release(); }
Glyph* D3DGlyphProvider::getGlyph(UChar ch, float emsize) { HRESULT hr = S_OK; IDWriteTextLayout* ptextlayout = NULL; ASSERT_PTR(mpTextFormat); hr = mpDWriteFactory->CreateTextLayout(&ch, 1, mpTextFormat, 512, 512, &ptextlayout); if ( SUCCEEDED(hr) ) { DWRITE_TEXT_RANGE range = { 0, 0 }; ptextlayout->SetFontSize(emsize, range); AutoPtr<Glyph> glyph = new Glyph(); hr = ptextlayout->Draw(glyph.getPointer(), mpTextRenderer, 0, 0); if ( SUCCEEDED(hr) ) { return glyph.release(); } } return NULL; }
void DrawShita(const Chat &chat) { RECT rc; rc.top = rcWnd_.bottom - yPitch_ - static_cast<LONG>(chat.line * (int)((float)yPitch_ * CNJIni::GetSettings()->commentLineMargin)); rc.bottom = rc.top + yPitch_; rc.left = 0; rc.right = rcWnd_.right; D2D1_RECT_F layoutRect = D2D1::RectF( static_cast<FLOAT>(rc.left) / dpiScaleX_, static_cast<FLOAT>(rc.top) / dpiScaleY_, static_cast<FLOAT>(rc.right) / dpiScaleX_, static_cast<FLOAT>(rc.bottom) / dpiScaleY_ ); pTextFormat_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); IDWriteTextLayout *pLayout; pDWriteFactory_->CreateTextLayout( chat.text.c_str(), chat.text.length(), pTextFormat_, static_cast<FLOAT>(rc.right) / dpiScaleX_, static_cast<FLOAT>(yPitch_) / dpiScaleY_, &pLayout ); D2D1_POINT_2F pt = {static_cast<FLOAT>(rc.left) / dpiScaleX_, static_cast<FLOAT>(rc.top) / dpiScaleY_}; ID2D1SolidColorBrush *pColor; pRT_->CreateSolidColorBrush( D2D1::ColorF(bgr2rgb(chat.color)), &pColor ); renderer_->SetColorBrush(pColor); pLayout->Draw(NULL, pTextRenderer_, pt.x, pt.y); pColor->Release(); pLayout->Release(); pTextFormat_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); }