Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try to fix unicode layout issue #3578

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions src/buffer/out/DbcsAttribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ class DbcsAttribute final
{
Single = 0x00,
Leading = 0x01,
Trailing = 0x02
Trailing = 0x02,
ZeroLeading = 0x03,
ZeroTrailing = 0x04,
};

DbcsAttribute() noexcept :
Expand Down Expand Up @@ -53,9 +55,19 @@ class DbcsAttribute final
return _attribute == Attribute::Trailing;
}

constexpr bool IsZeroLeading() const noexcept
{
return _attribute == Attribute::ZeroLeading;
}

constexpr bool IsZeroTrailing() const noexcept
{
return _attribute == Attribute::ZeroTrailing;
}

constexpr bool IsDbcs() const noexcept
{
return IsLeading() || IsTrailing();
return IsLeading() || IsTrailing() || IsZeroLeading() || IsZeroTrailing();
}

constexpr bool IsGlyphStored() const noexcept
Expand Down Expand Up @@ -83,6 +95,16 @@ class DbcsAttribute final
_attribute = Attribute::Trailing;
}

void SetZeroLeading() noexcept
{
_attribute = Attribute::ZeroLeading;
}

void SetZeroTrailing() noexcept
{
_attribute = Attribute::ZeroTrailing;
}

void Reset() noexcept
{
SetSingle();
Expand Down
32 changes: 29 additions & 3 deletions src/buffer/out/OutputCellIterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,17 @@ bool OutputCellIterator::_TryMoveTrailing() noexcept
_currentView.TextAttrBehavior());
return true;
}
else if (_currentView.DbcsAttr().IsZeroLeading())
{
auto dbcsAttr = _currentView.DbcsAttr();
dbcsAttr.SetZeroTrailing();

_currentView = OutputCellView(_currentView.Chars(),
dbcsAttr,
_currentView.TextAttr(),
_currentView.TextAttrBehavior());
return true;
}
else
{
return false;
Expand Down Expand Up @@ -410,10 +421,15 @@ OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
{
const auto glyph = Utf16Parser::ParseNext(view);
DbcsAttribute dbcsAttr;
if (IsGlyphFullWidth(glyph))
CodepointWidth width = GetGlyphWidth(glyph);
if (width == CodepointWidth::Wide)
{
dbcsAttr.SetLeading();
}
else if (width == CodepointWidth::Combining)
{
dbcsAttr.SetZeroLeading();
}

return OutputCellView(glyph, dbcsAttr, attr, behavior);
}
Expand All @@ -432,10 +448,15 @@ OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch) noexcept
const auto glyph = std::wstring_view(&wch, 1);

DbcsAttribute dbcsAttr;
if (IsGlyphFullWidth(wch))
CodepointWidth width = GetGlyphWidth(glyph);
if (width == CodepointWidth::Wide)
{
dbcsAttr.SetLeading();
}
else if (width == CodepointWidth::Combining)
{
dbcsAttr.SetZeroLeading();
}

return OutputCellView(glyph, dbcsAttr, InvalidTextAttribute, TextAttributeBehavior::Current);
}
Expand Down Expand Up @@ -469,10 +490,15 @@ OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const Text
const auto glyph = std::wstring_view(&wch, 1);

DbcsAttribute dbcsAttr;
if (IsGlyphFullWidth(wch))
CodepointWidth width = GetGlyphWidth(glyph);
if (width == CodepointWidth::Wide)
{
dbcsAttr.SetLeading();
}
else if (width == CodepointWidth::Combining)
{
dbcsAttr.SetZeroLeading();
}

return OutputCellView(glyph, dbcsAttr, attr, TextAttributeBehavior::Stored);
}
Expand Down
8 changes: 8 additions & 0 deletions src/buffer/out/OutputCellView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ size_t OutputCellView::Columns() const noexcept
{
return 1;
}
else if (DbcsAttr().IsZeroLeading())
{
return 0;
}
else if (DbcsAttr().IsZeroTrailing())
{
return 0;
}

return 1;
}
Expand Down
6 changes: 6 additions & 0 deletions src/renderer/base/Cluster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@

using namespace Microsoft::Console::Render;

Cluster::Cluster() :
_text(L""),
_columns(0)
{
}

// Routine Description:
// - Instantiates a new cluster structure
// Arguments:
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/base/renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,

// Advance the cluster and column counts.
const auto columnCount = clusters.back().GetColumns();
it += columnCount > 0 ? columnCount : 1; // prevent infinite loop for no visible columns
it += columnCount > 0 ? columnCount : 2; // prevent infinite loop for no visible columns
cols += columnCount;

} while (it);
Expand Down
65 changes: 59 additions & 6 deletions src/renderer/dx/CustomTextLayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
_localeName.resize(gsl::narrow_cast<size_t>(format->GetLocaleNameLength()) + 1); // +1 for null
THROW_IF_FAILED(format->GetLocaleName(_localeName.data(), gsl::narrow<UINT32>(_localeName.size())));

for (const auto& cluster : clusters)
for (auto& cluster : clusters)
{
const auto cols = gsl::narrow<UINT16>(cluster.GetColumns());
_textClusterColumns.push_back(cols);
_textClusters.push_back(cluster);
_text += cluster.GetText();
}
}
Expand Down Expand Up @@ -166,6 +166,30 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
}

_runs.swap(runs);

std::vector<std::vector<Cluster>> runClusters;
runClusters.reserve(totalRuns);

size_t clusterStart = 0;
for (size_t i = 0; i < _runs.size(); )
{
LinkedRun& currentRun = _runs.at(i);
std::vector<Cluster> currentRunClusters;
currentRunClusters.reserve(currentRun.glyphCount);

for (size_t j = clusterStart; j < currentRun.textLength; ++j)
{
Cluster cluster = _textClusters.at(j);
currentRunClusters.emplace_back(cluster);
++clusterStart;
}

runClusters.emplace_back(currentRunClusters);

i = currentRun.nextRunIndex;
}

_runClusters.swap(runClusters);
}
CATCH_RETURN();
return S_OK;
Expand Down Expand Up @@ -395,8 +419,26 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
DWRITE_FONT_METRICS1 metrics;
run.fontFace->GetMetrics(&metrics);

UINT32 glyphCount = run.glyphStart + run.glyphCount;
size_t clusterCount = _textClusters.size();
UINT32 runStartTemp = run.glyphStart;
UINT32 columnStart = 0;
while (runStartTemp > 0 && columnStart < _textClusters.size())
{
const auto cluster = _textClusters.at(columnStart);
if (cluster.GetColumns() > 0)
{
++columnStart;
--runStartTemp;
}
else
{
++columnStart;
}
};

// Walk through advances and space out characters that are too small to consume their box.
for (auto i = run.glyphStart; i < (run.glyphStart + run.glyphCount); i++)
for (UINT32 i = run.glyphStart, j = columnStart; i < glyphCount && j < clusterCount; j++)
{
// Advance is how wide in pixels the glyph is
auto& advance = _glyphAdvances.at(i);
Expand All @@ -405,11 +447,13 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
auto& offset = _glyphOffsets.at(i);

// Get how many columns we expected the glyph to have and mutiply into pixels.
const auto columns = _textClusterColumns.at(i);
const auto cluster = _textClusters.at(columnStart);
const auto columns = cluster.GetColumns();
const auto columnHasWidth = columns > 0;
const auto advanceExpected = static_cast<float>(columns * _width);

// If what we expect is bigger than what we have... pad it out.
if (advanceExpected > advance)
if (advanceExpected > advance && advance > FLT_EPSILON)
{
// Get the amount of space we have leftover.
const auto diff = advanceExpected - advance;
Expand All @@ -422,7 +466,7 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
advance = advanceExpected;
}
// If what we expect is smaller than what we have... rescale the font size to get a smaller glyph to fit.
else if (advanceExpected < advance)
else if (advanceExpected < advance && advanceExpected > FLT_EPSILON)
{
// We need to retrieve the design information for this specific glyph so we can figure out the appropriate
// height proportional to the width that we desire.
Expand All @@ -443,6 +487,15 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
// Set the advance to the perfect width that we want.
advance = advanceExpected;
}

if (columnHasWidth)
{
++i;
}
else
{
++clusterCount;
}
}

// Certain fonts, like Batang, contain glyphs for hidden control
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/dx/CustomTextLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ namespace Microsoft::Console::Render

// The text we're analyzing and processing into a layout
std::wstring _text;
std::vector<UINT16> _textClusterColumns;
std::vector<Cluster> _textClusters;
size_t _width;

// Properties of the text that might be relevant.
Expand All @@ -158,6 +158,7 @@ namespace Microsoft::Console::Render

// Text analysis results
std::vector<LinkedRun> _runs;
std::vector<std::vector<Cluster>> _runClusters;
std::vector<DWRITE_LINE_BREAKPOINT> _breakpoints;

// Text analysis interim status variable (to assist the Analyzer Sink in operations involving _runs)
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/inc/Cluster.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ namespace Microsoft::Console::Render
class Cluster
{
public:
Cluster();

Cluster(const std::wstring_view text, const size_t columns);

const wchar_t GetTextAsSingle() const noexcept;
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/vt/paint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ using namespace Microsoft::Console::Types;
for (const auto& cluster : clusters)
{
wstr.append(cluster.GetText());
RETURN_IF_FAILED(ShortAdd(totalWidth, gsl::narrow<short>(cluster.GetColumns()), &totalWidth));
RETURN_IF_FAILED(ShortAdd(totalWidth, gsl::narrow<short>(cluster.GetColumns() > 0 ? cluster.GetColumns() : 2), &totalWidth));
}

RETURN_IF_FAILED(VtEngine::_WriteTerminalAscii(wstr));
Expand Down Expand Up @@ -384,7 +384,7 @@ using namespace Microsoft::Console::Types;
for (const auto& cluster : clusters)
{
unclusteredString.append(cluster.GetText());
RETURN_IF_FAILED(ShortAdd(totalWidth, static_cast<short>(cluster.GetColumns()), &totalWidth));
RETURN_IF_FAILED(ShortAdd(totalWidth, static_cast<short>(cluster.GetColumns() > 0 ? cluster.GetColumns() : 2), &totalWidth));
}
const size_t cchLine = unclusteredString.size();

Expand Down
Loading