diff options
| author | Neil <nyamatongwe@gmail.com> | 2021-03-19 16:15:16 +1100 | 
|---|---|---|
| committer | Neil <nyamatongwe@gmail.com> | 2021-03-19 16:15:16 +1100 | 
| commit | 8b8a02b4bd250d28bc0f42287ca3d167b7210f39 (patch) | |
| tree | d8d42a94f5abdaf1247db4fc5bf766c9a72cfbe9 | |
| parent | 09534ab81f72ce766c8d13ea13216dd589d41a1c (diff) | |
| download | scintilla-mirror-8b8a02b4bd250d28bc0f42287ca3d167b7210f39.tar.gz | |
UTF-8 text drawing and measurement.
Move SurfaceGDI::WidthText to match declaration order.
| -rw-r--r-- | cocoa/PlatCocoa.h | 10 | ||||
| -rw-r--r-- | cocoa/PlatCocoa.mm | 98 | ||||
| -rwxr-xr-x | gtk/PlatGTK.cxx | 88 | ||||
| -rw-r--r-- | qt/ScintillaEditBase/PlatQt.cpp | 81 | ||||
| -rw-r--r-- | qt/ScintillaEditBase/PlatQt.h | 11 | ||||
| -rw-r--r-- | src/Platform.h | 7 | ||||
| -rw-r--r-- | win32/PlatWin.cxx | 293 | 
7 files changed, 554 insertions, 34 deletions
| diff --git a/cocoa/PlatCocoa.h b/cocoa/PlatCocoa.h index 1ddb203c7..b918e5ed0 100644 --- a/cocoa/PlatCocoa.h +++ b/cocoa/PlatCocoa.h @@ -104,6 +104,7 @@ public:  	void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;  	void Copy(PRectangle rc, Scintilla::Point from, Surface &surfaceSource) override;  	std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override; +  	void DrawTextNoClip(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore,  			    ColourDesired back) override;  	void DrawTextClipped(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, @@ -111,6 +112,15 @@ public:  	void DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;  	void MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) override;  	XYPOSITION WidthText(const Font *font_, std::string_view text) override; + +	void DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, +			    ColourDesired back) override; +	void DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, +			     ColourDesired back) override; +	void DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override; +	void MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) override; +	XYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) override; +  	XYPOSITION Ascent(const Font *font_) override;  	XYPOSITION Descent(const Font *font_) override;  	XYPOSITION InternalLeading(const Font *font_) override; diff --git a/cocoa/PlatCocoa.mm b/cocoa/PlatCocoa.mm index 2e3ab2d57..f84a335aa 100644 --- a/cocoa/PlatCocoa.mm +++ b/cocoa/PlatCocoa.mm @@ -1227,6 +1227,104 @@ XYPOSITION SurfaceImpl::WidthText(const Font *font_, std::string_view text) {  	return static_cast<XYPOSITION>(textLayout->MeasureStringWidth());  } +//-------------------------------------------------------------------------------------------------- + +void SurfaceImpl::DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +				 ColourDesired fore, ColourDesired back) { +	FillRectangle(rc, back); +	DrawTextTransparent(rc, font_, ybase, text, fore); +} + +//-------------------------------------------------------------------------------------------------- + +void SurfaceImpl::DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +				  ColourDesired fore, ColourDesired back) { +	CGContextSaveGState(gc); +	CGContextClipToRect(gc, PRectangleToCGRect(rc)); +	DrawTextNoClip(rc, font_, ybase, text, fore, back); +	CGContextRestoreGState(gc); +} + +//-------------------------------------------------------------------------------------------------- + +void SurfaceImpl::DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +				      ColourDesired fore) { +	QuartzTextStyle *style = TextStyleFromFont(font_); +	if (!style) { +		return; +	} +	const CFStringEncoding encoding = kCFStringEncodingUTF8; +	ColourDesired colour(fore.AsInteger()); +	CGColorRef color = CGColorCreateGenericRGB(colour.GetRed()/255.0, colour.GetGreen()/255.0, colour.GetBlue()/255.0, 1.0); + +	style->setCTStyleColour(color); + +	CGColorRelease(color); + +	textLayout->setText(text, encoding, style); +	textLayout->draw(gc, rc.left, ybase); +} + +//-------------------------------------------------------------------------------------------------- + +void SurfaceImpl::MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) { +	const QuartzTextStyle *style = TextStyleFromFont(font_); +	if (!style) { +		return; +	} +	const CFStringEncoding encoding = kCFStringEncodingUTF8; +	const CFStringEncoding encodingUsed = +		textLayout->setText(text, encoding, style); + +	CTLineRef mLine = textLayout->getCTLine(); +	assert(mLine); + +	if (encodingUsed != encoding) { +		// Switched to MacRoman to make work so treat as single byte encoding. +		for (int i=0; i<text.length(); i++) { +			CGFloat xPosition = CTLineGetOffsetForStringIndex(mLine, i+1, nullptr); +			positions[i] = static_cast<XYPOSITION>(xPosition); +		} +		return; +	} + +	// Map the widths given for UTF-16 characters back onto the UTF-8 input string +	CFIndex fit = textLayout->getStringLength(); +	int ui=0; +	int i=0; +	std::vector<CGFloat> linePositions(fit); +	GetPositions(mLine, linePositions); +	while (ui<fit) { +		const unsigned char uch = text[i]; +		const unsigned int byteCount = UTF8BytesOfLead[uch]; +		const int codeUnits = UTF16LengthFromUTF8ByteCount(byteCount); +		const CGFloat xPosition = linePositions[ui]; +		for (unsigned int bytePos=0; (bytePos<byteCount) && (i<text.length()); bytePos++) { +			positions[i++] = static_cast<XYPOSITION>(xPosition); +		} +		ui += codeUnits; +	} +	XYPOSITION lastPos = 0.0f; +	if (i > 0) +		lastPos = positions[i-1]; +	while (i<text.length()) { +		positions[i++] = lastPos; +	} +} + +XYPOSITION SurfaceImpl::WidthTextUTF8(const Font *font_, std::string_view text) { +	const QuartzTextStyle *style = TextStyleFromFont(font_); +	if (!style) { +		return 1; +	} +	textLayout->setText(text, kCFStringEncodingUTF8, style); + +	return static_cast<XYPOSITION>(textLayout->MeasureStringWidth()); +} + +//-------------------------------------------------------------------------------------------------- + +  // This string contains a good range of characters to test for size.  const char sizeString[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"  			  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; diff --git a/gtk/PlatGTK.cxx b/gtk/PlatGTK.cxx index 3f0918886..b9c049f1d 100755 --- a/gtk/PlatGTK.cxx +++ b/gtk/PlatGTK.cxx @@ -180,6 +180,14 @@ public:  	void DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;  	void MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) override;  	XYPOSITION WidthText(const Font *font_, std::string_view text) override; + +	void DrawTextBaseUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore); +	void DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; +	void DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; +	void DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override; +	void MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) override; +	XYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) override; +  	XYPOSITION Ascent(const Font *font_) override;  	XYPOSITION Descent(const Font *font_) override;  	XYPOSITION InternalLeading(const Font *font_) override; @@ -914,6 +922,86 @@ XYPOSITION SurfaceImpl::WidthText(const Font *font_, std::string_view text) {  	return 1;  } +void SurfaceImpl::DrawTextBaseUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +	ColourDesired fore) { +	PenColour(fore); +	if (context) { +		const XYPOSITION xText = rc.left; +		if (PFont(font_)->pfd) { +			pango_layout_set_text(layout, text.data(), text.length()); +			pango_layout_set_font_description(layout, PFont(font_)->pfd); +			pango_cairo_update_layout(context, layout); +			PangoLayoutLine *pll = pango_layout_get_line_readonly(layout, 0); +			cairo_move_to(context, xText, ybase); +			pango_cairo_show_layout_line(context, pll); +		} +	} +} + +void SurfaceImpl::DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +	ColourDesired fore, ColourDesired back) { +	FillRectangle(rc, back); +	DrawTextBaseUTF8(rc, font_, ybase, text, fore); +} + +// On GTK+, exactly same as DrawTextNoClip +void SurfaceImpl::DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +	ColourDesired fore, ColourDesired back) { +	FillRectangle(rc, back); +	DrawTextBaseUTF8(rc, font_, ybase, text, fore); +} + +void SurfaceImpl::DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +	ColourDesired fore) { +	// Avoid drawing spaces in transparent mode +	for (size_t i = 0; i < text.length(); i++) { +		if (text[i] != ' ') { +			DrawTextBaseUTF8(rc, font_, ybase, text, fore); +			return; +		} +	} +} + +void SurfaceImpl::MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) { +	if (PFont(font_)->pfd) { +		pango_layout_set_font_description(layout, PFont(font_)->pfd); +		// Simple and direct as UTF-8 is native Pango encoding +		int i = 0; +		pango_layout_set_text(layout, text.data(), text.length()); +		ClusterIterator iti(layout, text.length()); +		while (!iti.finished) { +			iti.Next(); +			const int places = iti.curIndex - i; +			while (i < iti.curIndex) { +				// Evenly distribute space among bytes of this cluster. +				// Would be better to find number of characters and then +				// divide evenly between characters with each byte of a character +				// being at the same position. +				positions[i] = iti.position - (iti.curIndex - 1 - i) * iti.distance / places; +				i++; +			} +		} +		PLATFORM_ASSERT(static_cast<size_t>(i) == text.length()); +	} else { +		// No font so return an ascending range of values +		for (size_t i = 0; i < text.length(); i++) { +			positions[i] = i + 1; +		} +	} +} + +XYPOSITION SurfaceImpl::WidthTextUTF8(const Font *font_, std::string_view text) { +	if (PFont(font_)->pfd) { +		pango_layout_set_font_description(layout, PFont(font_)->pfd); +		pango_layout_set_text(layout, text.data(), text.length()); +		PangoLayoutLine *pangoLine = pango_layout_get_line_readonly(layout, 0); +		PangoRectangle pos{}; +		pango_layout_line_get_extents(pangoLine, nullptr, &pos); +		return floatFromPangoUnits(pos.width); +	} +	return 1; +} +  // Ascent and descent determined by Pango font metrics.  XYPOSITION SurfaceImpl::Ascent(const Font *font_) { diff --git a/qt/ScintillaEditBase/PlatQt.cpp b/qt/ScintillaEditBase/PlatQt.cpp index 6a26d47c4..a1adf0a0d 100644 --- a/qt/ScintillaEditBase/PlatQt.cpp +++ b/qt/ScintillaEditBase/PlatQt.cpp @@ -557,6 +557,87 @@ XYPOSITION SurfaceImpl::WidthText(const Font *font, std::string_view text)  	return metrics.width(su);  } +void SurfaceImpl::DrawTextNoClipUTF8(PRectangle rc, +				 const Font *font, +				 XYPOSITION ybase, +				 std::string_view text, +				 ColourDesired fore, +				 ColourDesired back) +{ +	SetFont(font); +	PenColour(fore); + +	GetPainter()->setBackground(QColorFromCA(back)); +	GetPainter()->setBackgroundMode(Qt::OpaqueMode); +	QString su = QString::fromUtf8(text.data(), static_cast<int>(text.length())); +	GetPainter()->drawText(QPointF(rc.left, ybase), su); +} + +void SurfaceImpl::DrawTextClippedUTF8(PRectangle rc, +				  const Font *font, +				  XYPOSITION ybase, +				  std::string_view text, +				  ColourDesired fore, +				  ColourDesired back) +{ +	SetClip(rc); +	DrawTextNoClip(rc, font, ybase, text, fore, back); +	PopClip(); +} + +void SurfaceImpl::DrawTextTransparentUTF8(PRectangle rc, +				      const Font *font, +				      XYPOSITION ybase, +				      std::string_view text, +	ColourDesired fore) +{ +	SetFont(font); +	PenColour(fore); + +	GetPainter()->setBackgroundMode(Qt::TransparentMode); +	QString su = QString::fromUtf8(text.data(), static_cast<int>(text.length())); +	GetPainter()->drawText(QPointF(rc.left, ybase), su); +} + +void SurfaceImpl::MeasureWidthsUTF8(const Font *font, +				std::string_view text, +				XYPOSITION *positions) +{ +	if (!font) +		return; +	QString su = QString::fromUtf8(text.data(), static_cast<int>(text.length())); +	QTextLayout tlay(su, *FontPointer(font), GetPaintDevice()); +	tlay.beginLayout(); +	QTextLine tl = tlay.createLine(); +	tlay.endLayout(); +	int fit = su.size(); +	int ui=0; +	size_t i=0; +	while (ui<fit) { +		const unsigned char uch = text[i]; +		const unsigned int byteCount = UTF8BytesOfLead[uch]; +		const int codeUnits = UTF16LengthFromUTF8ByteCount(byteCount); +		qreal xPosition = tl.cursorToX(ui+codeUnits); +		for (size_t bytePos=0; (bytePos<byteCount) && (i<text.length()); bytePos++) { +			positions[i++] = xPosition; +		} +		ui += codeUnits; +	} +	XYPOSITION lastPos = 0; +	if (i > 0) +		lastPos = positions[i-1]; +	while (i<text.length()) { +		positions[i++] = lastPos; +	} +} + +XYPOSITION SurfaceImpl::WidthTextUTF8(const Font *font, std::string_view text) +{ +	QFontMetricsF metrics(*FontPointer(font), device); +	QString su = QString::fromUtf8(text.data(), static_cast<int>(text.length())); +	return metrics.width(su); +} +  XYPOSITION SurfaceImpl::Ascent(const Font *font)  {  	QFontMetricsF metrics(*FontPointer(font), device); diff --git a/qt/ScintillaEditBase/PlatQt.h b/qt/ScintillaEditBase/PlatQt.h index c690e7697..a743a4be5 100644 --- a/qt/ScintillaEditBase/PlatQt.h +++ b/qt/ScintillaEditBase/PlatQt.h @@ -126,6 +126,17 @@ public:  	void MeasureWidths(const Font *font, std::string_view text,  		XYPOSITION *positions) override;  	XYPOSITION WidthText(const Font *font, std::string_view text) override; + +	void DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, +		std::string_view text, ColourDesired fore, ColourDesired back) override; +	void DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, +		std::string_view text, ColourDesired fore, ColourDesired back) override; +	void DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, +		std::string_view text, ColourDesired fore) override; +	void MeasureWidthsUTF8(const Font *font_, std::string_view text, +		XYPOSITION *positions) override; +	XYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) override; +  	XYPOSITION Ascent(const Font *font) override;  	XYPOSITION Descent(const Font *font) override;  	XYPOSITION InternalLeading(const Font *font) override; diff --git a/src/Platform.h b/src/Platform.h index 3333d3d4c..45c04abdc 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -208,6 +208,13 @@ public:  	virtual void DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) = 0;  	virtual void MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) = 0;  	virtual XYPOSITION WidthText(const Font *font_, std::string_view text) = 0; + +	virtual void DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) = 0; +	virtual void DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) = 0; +	virtual void DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) = 0; +	virtual void MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) = 0; +	virtual XYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) = 0; +  	virtual XYPOSITION Ascent(const Font *font_)=0;  	virtual XYPOSITION Descent(const Font *font_)=0;  	virtual XYPOSITION InternalLeading(const Font *font_)=0; diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index 26a351871..3f93a96fd 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -506,6 +506,14 @@ public:  	void DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;  	void MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) override;  	XYPOSITION WidthText(const Font *font_, std::string_view text) override; + +	void DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions); +	void DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; +	void DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; +	void DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override; +	void MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) override; +	XYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) override; +  	XYPOSITION Ascent(const Font *font_) override;  	XYPOSITION Descent(const Font *font_) override;  	XYPOSITION InternalLeading(const Font *font_) override; @@ -1003,23 +1011,11 @@ void SurfaceGDI::DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITIO  	}  } -XYPOSITION SurfaceGDI::WidthText(const Font *font_, std::string_view text) { -	SetFont(font_); -	SIZE sz={0,0}; -	if (!unicodeMode) { -		::GetTextExtentPoint32A(hdc, text.data(), std::min(static_cast<int>(text.length()), maxLenText), &sz); -	} else { -		const TextWide tbuf(text, unicodeMode, codePage); -		::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz); -	} -	return static_cast<XYPOSITION>(sz.cx); -} -  void SurfaceGDI::MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) {  	// Zero positions to avoid random behaviour on failure.  	std::fill(positions, positions + text.length(), 0.0f);  	SetFont(font_); -	SIZE sz={0,0}; +	SIZE sz = { 0,0 };  	int fit = 0;  	int i = 0;  	const int len = static_cast<int>(text.length()); @@ -1037,7 +1033,7 @@ void SurfaceGDI::MeasureWidths(const Font *font_, std::string_view text, XYPOSIT  			if (byteCount == 4) {	// Non-BMP  				ui++;  			} -			for (unsigned int bytePos=0; (bytePos<byteCount) && (i<len); bytePos++) { +			for (unsigned int bytePos = 0; (bytePos < byteCount) && (i < len); bytePos++) {  				positions[i++] = static_cast<XYPOSITION>(poses.buffer[ui]);  			}  		} @@ -1047,14 +1043,102 @@ void SurfaceGDI::MeasureWidths(const Font *font_, std::string_view text, XYPOSIT  			// Eeek - a NULL DC or other foolishness could cause this.  			return;  		} -		while (i<fit) { +		while (i < fit) {  			positions[i] = static_cast<XYPOSITION>(poses.buffer[i]);  			i++;  		}  	}  	// If any positions not filled in then use the last position for them  	const XYPOSITION lastPos = (fit > 0) ? positions[fit - 1] : 0.0f; -	std::fill(positions+i, positions + text.length(), lastPos); +	std::fill(positions + i, positions + text.length(), lastPos); +} + +XYPOSITION SurfaceGDI::WidthText(const Font *font_, std::string_view text) { +	SetFont(font_); +	SIZE sz = { 0,0 }; +	if (!unicodeMode) { +		::GetTextExtentPoint32A(hdc, text.data(), std::min(static_cast<int>(text.length()), maxLenText), &sz); +	} else { +		const TextWide tbuf(text, unicodeMode, codePage); +		::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz); +	} +	return static_cast<XYPOSITION>(sz.cx); +} + +void SurfaceGDI::DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) { +	SetFont(font_); +	const RECT rcw = RectFromPRectangle(rc); +	const int x = static_cast<int>(rc.left); +	const int yBaseInt = static_cast<int>(ybase); + +	const TextWide tbuf(text, true); +	::ExtTextOutW(hdc, x, yBaseInt, fuOptions, &rcw, tbuf.buffer, tbuf.tlen, nullptr); +} + +void SurfaceGDI::DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +	ColourDesired fore, ColourDesired back) { +	::SetTextColor(hdc, fore.AsInteger()); +	::SetBkColor(hdc, back.AsInteger()); +	DrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE); +} + +void SurfaceGDI::DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +	ColourDesired fore, ColourDesired back) { +	::SetTextColor(hdc, fore.AsInteger()); +	::SetBkColor(hdc, back.AsInteger()); +	DrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED); +} + +void SurfaceGDI::DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +	ColourDesired fore) { +	// Avoid drawing spaces in transparent mode +	for (const char ch : text) { +		if (ch != ' ') { +			::SetTextColor(hdc, fore.AsInteger()); +			::SetBkMode(hdc, TRANSPARENT); +			DrawTextCommonUTF8(rc, font_, ybase, text, 0); +			::SetBkMode(hdc, OPAQUE); +			return; +		} +	} +} + +void SurfaceGDI::MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) { +	// Zero positions to avoid random behaviour on failure. +	std::fill(positions, positions + text.length(), 0.0f); +	SetFont(font_); +	SIZE sz = { 0,0 }; +	int fit = 0; +	int i = 0; +	const int len = static_cast<int>(text.length()); +	const TextWide tbuf(text, true); +	TextPositionsI poses(tbuf.tlen); +	if (!::GetTextExtentExPointW(hdc, tbuf.buffer, tbuf.tlen, maxWidthMeasure, &fit, poses.buffer, &sz)) { +		// Failure +		return; +	} +	// Map the widths given for UTF-16 characters back onto the UTF-8 input string +	for (int ui = 0; ui < fit; ui++) { +		const unsigned char uch = text[i]; +		const unsigned int byteCount = UTF8BytesOfLead[uch]; +		if (byteCount == 4) {	// Non-BMP +			ui++; +		} +		for (unsigned int bytePos = 0; (bytePos < byteCount) && (i < len); bytePos++) { +			positions[i++] = static_cast<XYPOSITION>(poses.buffer[ui]); +		} +	} +	// If any positions not filled in then use the last position for them +	const XYPOSITION lastPos = (fit > 0) ? positions[fit - 1] : 0.0f; +	std::fill(positions + i, positions + text.length(), lastPos); +} + +XYPOSITION SurfaceGDI::WidthTextUTF8(const Font *font_, std::string_view text) { +	SetFont(font_); +	SIZE sz = { 0,0 }; +	const TextWide tbuf(text, true); +	::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz); +	return static_cast<XYPOSITION>(sz.cx);  }  XYPOSITION SurfaceGDI::Ascent(const Font *font_) { @@ -1208,6 +1292,14 @@ public:  	void DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;  	void MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) override;  	XYPOSITION WidthText(const Font *font_, std::string_view text) override; + +	void DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions); +	void DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; +	void DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override; +	void DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override; +	void MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) override; +	XYPOSITION WidthTextUTF8(const Font *font_, std::string_view text) override; +  	XYPOSITION Ascent(const Font *font_) override;  	XYPOSITION Descent(const Font *font_) override;  	XYPOSITION InternalLeading(const Font *font_) override; @@ -2104,24 +2196,6 @@ void SurfaceD2D::DrawTextTransparent(PRectangle rc, const Font *font_, XYPOSITIO  	}  } -XYPOSITION SurfaceD2D::WidthText(const Font *font_, std::string_view text) { -	FLOAT width = 1.0; -	SetFont(font_); -	const TextWide tbuf(text, unicodeMode, codePageText); -	if (pIDWriteFactory && pTextFormat) { -		// Create a layout -		IDWriteTextLayout *pTextLayout = nullptr; -		const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 1000.0, 1000.0, &pTextLayout); -		if (SUCCEEDED(hr) && pTextLayout) { -			DWRITE_TEXT_METRICS textMetrics; -			if (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics))) -				width = textMetrics.widthIncludingTrailingWhitespace; -			ReleaseUnknown(pTextLayout); -		} -	} -	return width; -} -  void SurfaceD2D::MeasureWidths(const Font *font_, std::string_view text, XYPOSITION *positions) {  	SetFont(font_);  	if (!pIDWriteFactory || !pTextFormat) { @@ -2203,6 +2277,157 @@ void SurfaceD2D::MeasureWidths(const Font *font_, std::string_view text, XYPOSIT  	}  } +XYPOSITION SurfaceD2D::WidthText(const Font *font_, std::string_view text) { +	FLOAT width = 1.0; +	SetFont(font_); +	const TextWide tbuf(text, unicodeMode, codePageText); +	if (pIDWriteFactory && pTextFormat) { +		// Create a layout +		IDWriteTextLayout *pTextLayout = nullptr; +		const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 1000.0, 1000.0, &pTextLayout); +		if (SUCCEEDED(hr) && pTextLayout) { +			DWRITE_TEXT_METRICS textMetrics; +			if (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics))) +				width = textMetrics.widthIncludingTrailingWhitespace; +			ReleaseUnknown(pTextLayout); +		} +	} +	return width; +} + +void SurfaceD2D::DrawTextCommonUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) { +	SetFont(font_); + +	// Use Unicode calls +	const TextWide tbuf(text, true); +	if (pRenderTarget && pTextFormat && pBrush) { +		if (fuOptions & ETO_CLIPPED) { +			D2D1_RECT_F rcClip = { rc.left, rc.top, rc.right, rc.bottom }; +			pRenderTarget->PushAxisAlignedClip(rcClip, D2D1_ANTIALIAS_MODE_ALIASED); +		} + +		// Explicitly creating a text layout appears a little faster +		IDWriteTextLayout *pTextLayout = nullptr; +		const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, +			rc.Width(), rc.Height(), &pTextLayout); +		if (SUCCEEDED(hr)) { +			D2D1_POINT_2F origin = {rc.left, ybase-yAscent}; +			pRenderTarget->DrawTextLayout(origin, pTextLayout, pBrush, d2dDrawTextOptions); +			ReleaseUnknown(pTextLayout); +		} + +		if (fuOptions & ETO_CLIPPED) { +			pRenderTarget->PopAxisAlignedClip(); +		} +	} +} + +void SurfaceD2D::DrawTextNoClipUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +	ColourDesired fore, ColourDesired back) { +	if (pRenderTarget) { +		FillRectangle(rc, back); +		D2DPenColour(fore); +		DrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE); +	} +} + +void SurfaceD2D::DrawTextClippedUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +	ColourDesired fore, ColourDesired back) { +	if (pRenderTarget) { +		FillRectangle(rc, back); +		D2DPenColour(fore); +		DrawTextCommonUTF8(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED); +	} +} + +void SurfaceD2D::DrawTextTransparentUTF8(PRectangle rc, const Font *font_, XYPOSITION ybase, std::string_view text, +	ColourDesired fore) { +	// Avoid drawing spaces in transparent mode +	for (const char ch : text) { +		if (ch != ' ') { +			if (pRenderTarget) { +				D2DPenColour(fore); +				DrawTextCommonUTF8(rc, font_, ybase, text, 0); +			} +			return; +		} +	} +} + +void SurfaceD2D::MeasureWidthsUTF8(const Font *font_, std::string_view text, XYPOSITION *positions) { +	SetFont(font_); +	if (!pIDWriteFactory || !pTextFormat) { +		// SetFont failed or no access to DirectWrite so give up. +		return; +	} +	const TextWide tbuf(text, true); +	TextPositions poses(tbuf.tlen); +	// Initialize poses for safety. +	std::fill(poses.buffer, poses.buffer + tbuf.tlen, 0.0f); +	// Create a layout +	IDWriteTextLayout *pTextLayout = nullptr; +	const HRESULT hrCreate = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 10000.0, 1000.0, &pTextLayout); +	if (!SUCCEEDED(hrCreate)) { +		return; +	} +	constexpr int clusters = stackBufferLength; +	DWRITE_CLUSTER_METRICS clusterMetrics[clusters]; +	UINT32 count = 0; +	const HRESULT hrGetCluster = pTextLayout->GetClusterMetrics(clusterMetrics, clusters, &count); +	ReleaseUnknown(pTextLayout); +	if (!SUCCEEDED(hrGetCluster)) { +		return; +	} +	// A cluster may be more than one WCHAR, such as for "ffi" which is a ligature in the Candara font +	FLOAT position = 0.0f; +	size_t ti = 0; +	for (size_t ci = 0; ci < count; ci++) { +		for (size_t inCluster = 0; inCluster < clusterMetrics[ci].length; inCluster++) { +			poses.buffer[ti++] = position + clusterMetrics[ci].width * (inCluster + 1) / clusterMetrics[ci].length; +		} +		position += clusterMetrics[ci].width; +	} +	PLATFORM_ASSERT(ti == static_cast<size_t>(tbuf.tlen)); +	// Map the widths given for UTF-16 characters back onto the UTF-8 input string +	int ui = 0; +	size_t i = 0; +	while (ui < tbuf.tlen) { +		const unsigned char uch = text[i]; +		const unsigned int byteCount = UTF8BytesOfLead[uch]; +		if (byteCount == 4) {	// Non-BMP +			ui++; +		} +		for (unsigned int bytePos = 0; (bytePos < byteCount) && (i < text.length()); bytePos++) { +			positions[i++] = poses.buffer[ui]; +		} +		ui++; +	} +	XYPOSITION lastPos = 0.0f; +	if (i > 0) +		lastPos = positions[i - 1]; +	while (i < text.length()) { +		positions[i++] = lastPos; +	} +} + +XYPOSITION SurfaceD2D::WidthTextUTF8(const Font * font_, std::string_view text) { +	FLOAT width = 1.0; +	SetFont(font_); +	const TextWide tbuf(text, true); +	if (pIDWriteFactory && pTextFormat) { +		// Create a layout +		IDWriteTextLayout *pTextLayout = nullptr; +		const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 1000.0, 1000.0, &pTextLayout); +		if (SUCCEEDED(hr)) { +			DWRITE_TEXT_METRICS textMetrics; +			if (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics))) +				width = textMetrics.widthIncludingTrailingWhitespace; +			ReleaseUnknown(pTextLayout); +		} +	} +	return width; +} +  XYPOSITION SurfaceD2D::Ascent(const Font *font_) {  	SetFont(font_);  	return std::ceil(yAscent); | 
