From cdecfcd67f8929350bd7713ed7978f5294fcfe38 Mon Sep 17 00:00:00 2001 From: Alex Alabuzhev Date: Thu, 8 Jul 2021 16:35:11 +0100 Subject: [PATCH] #10477: Handle things above U+FFFF in GDI renderer (#10580) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Implementation of #10477 - handle surrogate pairs in GDI renderer. ## PR Checklist * [ ] Closes #10477 * [x] CLA signed. * [ ] Tests added/passed * [ ] Documentation updated. * [ ] Schema updated. * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #10477 ## Detailed Description of the Pull Request / Additional comments ### Why not let Windows draw surrogate pairs? It can do that. Basically, the comment says everything: https://github.com/microsoft/terminal/blob/c90de692509b074bfde191910d67154cfe389911/src/renderer/gdi/paint.cpp#L346-L347 However, handling things above U+FFFF doesn't really require extra effort. It's enough to: - Put *all* characters to the output buffer - Set the first width to cluster width and the rest to 0 - Sit back and relax while Windows does the rest ## Validation Steps Performed ```CMD @echo off chcp 65001 echo 𠜎𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼𠵿𠸎 echo 👨👩👧👦 ``` Save this as a UTF-8 cmd file and run. ### Before the change ![image](https://user-images.githubusercontent.com/11453922/122832196-ed438880-d2e2-11eb-93dd-931954efedbf.png) ### After the change ![image](https://user-images.githubusercontent.com/11453922/122832217-f2a0d300-d2e2-11eb-99f0-e129e5544667.png) An example of a third party app working with surrogate pairs in a patched OpenConsole: ![image](https://user-images.githubusercontent.com/11453922/122838225-837cac00-d2ed-11eb-8faf-dbeb52f77916.png) As discussed, this change doesn't claim to be the full support for surrogate pairs (there are still corner cases possible), but brings it on par with Terminal with minimal effort. --- src/renderer/gdi/paint.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 5c948efaac3..089e9777e69 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -328,11 +328,13 @@ using namespace Microsoft::Console::Render; const auto pPolyTextLine = &_pPolyText[_cPolyText]; - auto& polyString = _polyStrings.emplace_back(cchLine, UNICODE_NULL); + auto& polyString = _polyStrings.emplace_back(); + polyString.reserve(cchLine); COORD const coordFontSize = _GetFontSize(); - auto& polyWidth = _polyWidths.emplace_back(cchLine, 0); + auto& polyWidth = _polyWidths.emplace_back(); + polyWidth.reserve(cchLine); // Sum up the total widths the entire line/run is expected to take while // copying the pixel widths into a structure to direct GDI how many pixels to use per character. @@ -343,11 +345,11 @@ using namespace Microsoft::Console::Render; { const auto& cluster = til::at(clusters, i); - // Our GDI renderer hasn't and isn't going to handle things above U+FFFF or sequences. - // So replace anything complicated with a replacement character for drawing purposes. - polyString[i] = cluster.GetTextAsSingle(); - polyWidth[i] = gsl::narrow(cluster.GetColumns()) * coordFontSize.X; - cchCharWidths += polyWidth[i]; + const auto text = cluster.GetText(); + polyString += text; + polyWidth.push_back(gsl::narrow(cluster.GetColumns()) * coordFontSize.X); + cchCharWidths += polyWidth.back(); + polyWidth.append(text.size() - 1, 0); } // Detect and convert for raster font... @@ -396,7 +398,7 @@ using namespace Microsoft::Console::Render; const auto bottomOffset = _currentLineRendition == LineRendition::DoubleHeightTop ? halfHeight : 0; pPolyTextLine->lpstr = polyString.data(); - pPolyTextLine->n = gsl::narrow(clusters.size()); + pPolyTextLine->n = gsl::narrow(polyString.size()); pPolyTextLine->x = ptDraw.x; pPolyTextLine->y = ptDraw.y; pPolyTextLine->uiFlags = ETO_OPAQUE | ETO_CLIPPED;