3
3
using System . Diagnostics . CodeAnalysis ;
4
4
using System . Linq ;
5
5
using System . Management . Automation ;
6
+ using System . Management . Automation . Host ;
6
7
using System . Management . Automation . Language ;
7
8
using System . Runtime . InteropServices ;
8
9
using System . Security ;
9
10
using PSConsoleUtilities . Internal ;
11
+ using ConsoleHandle = Microsoft . Win32 . SafeHandles . SafeFileHandle ;
12
+ using System . ComponentModel ;
10
13
11
14
namespace PSConsoleUtilities
12
15
{
@@ -27,6 +30,30 @@ public partial class PSConsoleReadLine
27
30
private uint _codePage ;
28
31
private bool _istmInitialized = false ;
29
32
private TEXTMETRIC _tm = new TEXTMETRIC ( ) ;
33
+ private bool _trueTypeInUse = false ;
34
+
35
+ private readonly Lazy < ConsoleHandle > _outputHandle = new Lazy < ConsoleHandle > ( ( ) =>
36
+ {
37
+ // We use CreateFile here instead of GetStdWin32Handle, as GetStdWin32Handle will return redirected handles
38
+ var handle = NativeMethods . CreateFile (
39
+ "CONOUT$" ,
40
+ ( UInt32 ) ( AccessQualifiers . GenericRead | AccessQualifiers . GenericWrite ) ,
41
+ ( UInt32 ) ShareModes . ShareWrite ,
42
+ ( IntPtr ) 0 ,
43
+ ( UInt32 ) CreationDisposition . OpenExisting ,
44
+ 0 ,
45
+ ( IntPtr ) 0 ) ;
46
+
47
+ if ( handle == NativeMethods . INVALID_HANDLE_VALUE )
48
+ {
49
+ int err = Marshal . GetLastWin32Error ( ) ;
50
+ Win32Exception innerException = new Win32Exception ( err ) ;
51
+ throw new Exception ( "Failed to retreive the input console handle." , innerException ) ;
52
+ }
53
+
54
+ return new ConsoleHandle ( handle , true ) ;
55
+ }
56
+ ) ;
30
57
31
58
private class SavedTokenState
32
59
{
@@ -83,43 +110,50 @@ private void ReallyRender()
83
110
var text = ParseInput ( ) ;
84
111
_codePage = NativeMethods . GetConsoleOutputCP ( ) ;
85
112
_istmInitialized = false ;
113
+ ConsoleHandle consoleHandle = _outputHandle . Value ;
114
+ CONSOLE_FONT_INFO_EX fontInfo = GetConsoleFontInfo ( consoleHandle ) ;
115
+ int fontType = fontInfo . FontFamily & NativeMethods . FontTypeMask ;
116
+ _trueTypeInUse = ( fontType & NativeMethods . TrueTypeFont ) == NativeMethods . TrueTypeFont ;
86
117
87
118
int statusLineCount = GetStatusLineCount ( ) ;
88
- int bufferLineCount = ConvertOffsetToCoordinates ( text . Length ) . Y - _initialY + 1 + statusLineCount ;
119
+ int j = _initialX + ( _bufferWidth * Options . ExtraPromptLineCount ) ;
120
+ var backgroundColor = _initialBackgroundColor ;
121
+ var foregroundColor = _initialForegroundColor ;
122
+ bool afterLastToken = false ;
123
+ int totalBytes = j ;
89
124
int bufferWidth = Console . BufferWidth ;
90
- if ( _consoleBuffer . Length != bufferLineCount * bufferWidth )
91
- {
92
- var newBuffer = new CHAR_INFO [ bufferLineCount * bufferWidth ] ;
93
- Array . Copy ( _consoleBuffer , newBuffer , _initialX + ( Options . ExtraPromptLineCount * _bufferWidth ) ) ;
94
- if ( _consoleBuffer . Length > bufferLineCount * bufferWidth )
95
- {
96
- int consoleBufferOffset = ConvertOffsetToConsoleBufferOffset ( text . Length , _initialX + ( Options . ExtraPromptLineCount * _bufferWidth ) ) ;
97
- // Need to erase the extra lines that we won't draw again
98
- for ( int i = consoleBufferOffset ; i < _consoleBuffer . Length ; i ++ )
99
- {
100
- _consoleBuffer [ i ] = _space ;
101
- }
102
- WriteBufferLines ( _consoleBuffer , ref _initialY ) ;
103
- }
104
- _consoleBuffer = newBuffer ;
105
- }
106
125
107
126
var tokenStack = new Stack < SavedTokenState > ( ) ;
108
127
tokenStack . Push ( new SavedTokenState
109
128
{
110
- Tokens = _tokens ,
111
- Index = 0 ,
129
+ Tokens = _tokens ,
130
+ Index = 0 ,
112
131
BackgroundColor = _initialBackgroundColor ,
113
132
ForegroundColor = _initialForegroundColor
114
133
} ) ;
115
134
116
- int j = _initialX + ( _bufferWidth * Options . ExtraPromptLineCount ) ;
117
- var backgroundColor = _initialBackgroundColor ;
118
- var foregroundColor = _initialForegroundColor ;
119
- bool afterLastToken = false ;
120
- int totalBytes = j ;
135
+ int bufferLineCount ;
136
+
121
137
try
122
138
{
139
+ bufferLineCount = ConvertOffsetToCoordinates ( text . Length ) . Y - _initialY + 1 + statusLineCount ;
140
+ if ( _consoleBuffer . Length != bufferLineCount * bufferWidth )
141
+ {
142
+ var newBuffer = new CHAR_INFO [ bufferLineCount * bufferWidth ] ;
143
+ Array . Copy ( _consoleBuffer , newBuffer , _initialX + ( Options . ExtraPromptLineCount * _bufferWidth ) ) ;
144
+ if ( _consoleBuffer . Length > bufferLineCount * bufferWidth )
145
+ {
146
+ int consoleBufferOffset = ConvertOffsetToConsoleBufferOffset ( text . Length , _initialX + ( Options . ExtraPromptLineCount * _bufferWidth ) ) ;
147
+ // Need to erase the extra lines that we won't draw again
148
+ for ( int i = consoleBufferOffset ; i < _consoleBuffer . Length ; i ++ )
149
+ {
150
+ _consoleBuffer [ i ] = _space ;
151
+ }
152
+ WriteBufferLines ( _consoleBuffer , ref _initialY ) ;
153
+ }
154
+ _consoleBuffer = newBuffer ;
155
+ }
156
+
123
157
for ( int i = 0 ; i < text . Length ; i ++ )
124
158
{
125
159
SavedTokenState state = null ;
@@ -221,6 +255,17 @@ private void ReallyRender()
221
255
MaybeEmphasize ( ref _consoleBuffer [ j ++ ] , i , foregroundColor , backgroundColor ) ;
222
256
223
257
}
258
+ else if ( size > 1 && IsCJKOutputCodePage ( ) && _trueTypeInUse )
259
+ {
260
+ _consoleBuffer [ j ] . UnicodeChar = text [ i ] ;
261
+ _consoleBuffer [ j ] . Attributes = ( ushort ) ( ( uint ) _consoleBuffer [ j ] . Attributes |
262
+ ( uint ) CHAR_INFO_Attributes . COMMON_LVB_LEADING_BYTE ) ;
263
+ MaybeEmphasize ( ref _consoleBuffer [ j ++ ] , i , foregroundColor , backgroundColor ) ;
264
+ _consoleBuffer [ j ] . UnicodeChar = text [ i ] ;
265
+ _consoleBuffer [ j ] . Attributes = ( ushort ) ( ( uint ) _consoleBuffer [ j ] . Attributes |
266
+ ( uint ) CHAR_INFO_Attributes . COMMON_LVB_TRAILING_BYTE ) ;
267
+ MaybeEmphasize ( ref _consoleBuffer [ j ++ ] , i , foregroundColor , backgroundColor ) ;
268
+ }
224
269
else
225
270
{
226
271
_consoleBuffer [ j ] . UnicodeChar = text [ i ] ;
@@ -476,6 +521,22 @@ private bool InRegion(int i)
476
521
return i >= start && i < end ;
477
522
}
478
523
524
+ private static CONSOLE_FONT_INFO_EX GetConsoleFontInfo ( ConsoleHandle consoleHandle )
525
+ {
526
+
527
+ CONSOLE_FONT_INFO_EX fontInfo = new CONSOLE_FONT_INFO_EX ( ) ;
528
+ fontInfo . cbSize = Marshal . SizeOf ( fontInfo ) ;
529
+ bool result = NativeMethods . GetCurrentConsoleFontEx ( consoleHandle . DangerousGetHandle ( ) , false , ref fontInfo ) ;
530
+
531
+ if ( result == false )
532
+ {
533
+ int err = Marshal . GetLastWin32Error ( ) ;
534
+ Win32Exception innerException = new Win32Exception ( err ) ;
535
+ throw new Exception ( "Failed to get console font information." , innerException ) ;
536
+ }
537
+ return fontInfo ;
538
+ }
539
+
479
540
private void MaybeEmphasize ( ref CHAR_INFO charInfo , int i , ConsoleColor foregroundColor , ConsoleColor backgroundColor )
480
541
{
481
542
if ( i >= _emphasisStart && i < ( _emphasisStart + _emphasisLength ) )
@@ -564,6 +625,18 @@ private uint CodePageToCharSet()
564
625
return csi . ciCharset ;
565
626
}
566
627
628
+ /// <summary>
629
+ /// Check if the output buffer code page is Japanese, Simplified Chinese, Korean, or Traditional Chinese
630
+ /// </summary>
631
+ /// <returns>true if it is CJK code page; otherwise, false.</returns>
632
+ private bool IsCJKOutputCodePage ( )
633
+ {
634
+ return _codePage == 932 || // Japanese
635
+ _codePage == 936 || // Simplified Chinese
636
+ _codePage == 949 || // Korean
637
+ _codePage == 950 ; // Traditional Chinese
638
+ }
639
+
567
640
private bool IsAvailableFarEastCodePage ( )
568
641
{
569
642
uint charSet = CodePageToCharSet ( ) ;
@@ -751,6 +824,10 @@ private int ConvertOffsetToConsoleBufferOffset(int offset, int startIndex)
751
824
{
752
825
j += 2 ;
753
826
}
827
+ else if ( LengthInBufferCells ( _buffer [ i ] ) > 1 && IsCJKOutputCodePage ( ) && _trueTypeInUse )
828
+ {
829
+ j += 2 ;
830
+ }
754
831
else
755
832
{
756
833
j ++ ;
0 commit comments