93
93
def __init__(self, stream):
94
94
self.stream = stream or sys.stdout
95
95
self.isatty = getattr(self.stream, 'isatty', lambda : False)()
96
force_ansi = os.environ.has_key('CALIBRE_FORCE_ANSI')
96
force_ansi = 'CALIBRE_FORCE_ANSI' in os.environ
97
97
if not self.isatty and force_ansi:
99
99
self.isansi = force_ansi or not iswindows
100
self.set_console = None
100
self.set_console = self.write_console = None
101
self.is_console = False
101
102
if not self.isansi:
104
105
self.msvcrt = msvcrt
105
106
self.file_handle = msvcrt.get_osfhandle(self.stream.fileno())
106
from ctypes import windll
107
self.set_console = windll.kernel32.SetConsoleTextAttribute
107
from ctypes import windll, wintypes, byref, c_wchar_p, c_size_t, POINTER, WinDLL
108
mode = wintypes.DWORD(0)
109
f = windll.kernel32.GetConsoleMode
110
f.argtypes, f.restype = [wintypes.HANDLE, POINTER(wintypes.DWORD)], wintypes.BOOL
111
if f(self.file_handle, byref(mode)):
112
# Stream is a console
113
self.set_console = windll.kernel32.SetConsoleTextAttribute
114
self.wcslen = crt().wcslen
115
self.wcslen.argtypes, self.wcslen.restype = [c_wchar_p], c_size_t
116
self.write_console = WinDLL('kernel32', use_last_error=True).WriteConsoleW
117
self.write_console.argtypes = [wintypes.HANDLE, wintypes.c_wchar_p, wintypes.DWORD, POINTER(wintypes.DWORD), wintypes.LPVOID]
118
self.write_console.restype = wintypes.BOOL
119
self.is_console = True
123
def write_unicode_text(self, text, ignore_errors=False):
124
' Windows only method that writes unicode strings correctly to the windows console using the Win32 API '
126
from ctypes import wintypes, byref, c_wchar_p
127
written = wintypes.DWORD(0)
130
t, text = text[:chunk], text[chunk:]
132
if not self.write_console(self.file_handle, wt, self.wcslen(wt), byref(written), None):
133
# Older versions of windows can fail to write large strings
134
# to console with WriteConsoleW (seen it happen on Win XP)
135
import ctypes, winerror
136
err = ctypes.get_last_error()
137
if err == winerror.ERROR_NOT_ENOUGH_MEMORY and chunk >= 128:
138
# Retry with a smaller chunk size (give up if chunk < 128)
142
if err == winerror.ERROR_GEN_FAILURE:
143
# On newer windows, this happens when trying to write
144
# non-ascii chars to the console and the console is set
145
# to use raster fonts (the default). In this case
146
# rather than failing, write an informative error
147
# message and the asciized version of the text.
148
print ('Non-ASCII text detected. You must set your Console\'s font to'
149
' Lucida Console or Consolas or some other TrueType font to see this text', file=self.stream, end=' -- ')
150
from calibre.utils.filenames import ascii_text
151
print (ascii_text(t + text), file=self.stream, end='')
153
if not ignore_errors:
154
raise ctypes.WinError(err)
158
# We use the C runtime bundled with the calibre windows build
162
d = os.path.join(os.path.dirname(sys.executable), '*.CRT', 'msvcr*.dll')
163
_crt = ctypes.CDLL(glob.glob(d)[0])
111
166
class ColoredStream(Detect):
113
168
def __init__(self, stream=None, fg=None, bg=None, bold=False):
180
236
self.convert_ansi(*match.groups())
182
238
self.write_plain_text(text, cursor, len(text))
184
241
def write_plain_text(self, text, start, end):
186
self.stream.write(text[start:end])
243
text = text[start:end]
244
if self.is_console and isinstance(text, bytes):
246
utext = text.decode(self.encoding)
250
return self.write_unicode_text(utext)
251
self.stream.write(text)
189
253
def convert_ansi(self, paramstring, command):
190
254
params = self.extract_params(paramstring)