ANSI support for the Windows CMD terminal is only available with Windows 10 version 1511 or newer. But even then, it’s not enable by default, for compatibility reasons. Native executables must have been marked as using ANSI or explicitly invoke a dedicated Windows function to enable ANSI support.
Neither applies to java.exe
. Therefore, you don’t get ANSI support.
You could work-around this by delegating the printing to another software which has ANSI support enable, like the echo
command.
import java.io.IOException;
public class ConsoleOutput {
public static final String ANSI_RESET_ALL = "\33[0m";
public static final String ANSI_YELLOW_FG = "\33[33m";
public static final String ANSI_RESET_FG = "\33[39m";
// Main driver method
public static void main(String[] args) {
// Printing the text on console prior adding
// the desired color
println(ANSI_YELLOW_FG + "This text is yellow" + ANSI_RESET_FG);
println("This is normal color");
}
static void println(String s) {
try {
new ProcessBuilder("cmd", "/c", "echo " + s).inheritIO().start().waitFor();
} catch (InterruptedException|IOException e) {
throw new RuntimeException(e);
}
}
}
For large text fragments, you could dump them into a temporary file and use the type
command to transfer them to the terminal.
Future Java versions will come with support to invoke native functions without the need for native helper code, like a JNI stub. With JDK 19’s preview version, the code to enable ANSI from within Java would look like
import java.lang.foreign.*;
import java.lang.invoke.*;
public class ConsoleOutput {
public static final String ANSI_RESET_ALL = "\33[0m";
public static final String ANSI_YELLOW_FG = "\33[33m";
public static final String ANSI_RESET_FG = "\33[39m";
public static void main(String[] args) {
if(initANSI()) System.out.println("ANSI should now work");
System.out.println(ANSI_YELLOW_FG + "This text is yellow" + ANSI_RESET_FG);
System.out.println("This is normal color");
}
static boolean initANSI() {
try {
SymbolLookup sl = SymbolLookup.libraryLookup("kernel32.dll", MemorySession.global());
Linker linker = Linker.nativeLinker();
MethodHandle GetStdHandle = linker.downcallHandle(sl.lookup("GetStdHandle").get(),
FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_INT)
);
MethodHandle SetConsoleMode = linker.downcallHandle(sl.lookup("SetConsoleMode").get(),
FunctionDescriptor.of(ValueLayout.JAVA_BOOLEAN, ValueLayout.ADDRESS, ValueLayout.JAVA_INT)
);
Addressable a = (MemoryAddress) GetStdHandle.invokeExact(STD_OUTPUT_HANDLE);
return (boolean)SetConsoleMode.invokeExact(a,
ENABLE_PROCESSED_OUTPUT|ENABLE_VIRTUAL_TERMINAL_PROCESSING);
} catch (RuntimeException | Error unchecked) {
throw unchecked;
} catch(Throwable e) {
throw new AssertionError(e);
}
}
static final int STD_OUTPUT_HANDLE = -11,
ENABLE_PROCESSED_OUTPUT = 0x0001, ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
}