# Canvas5 Theme

**URL:** <https://resource.dopus.com/t/canvas5-theme/59351>\
**Category:** Tools\
**Created:** [May 7, 2026, 11:54am UTC](https://resource.dopus.com/t/canvas5-theme/59351 "2026-05-07T11:54:07Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![jfour18](https://resource.dopus.com/user_avatar/resource.dopus.com/jfour18/32/66032_2.png) [@jfour18](https://resource.dopus.com/u/jfour18)\
**Post date:** [May 7, 2026, 11:54am UTC](https://resource.dopus.com/t/canvas5-theme/59351/1 "2026-05-07T11:54:07Z")

</div>

This script uses PowerShell to change Windows System Color Index 5 (Canvas 5) from the glaring white to any color of your choosing. This changes the the default white canvas in word, excel, outlook, and many other programs , therefore it should be USED WITH CAUTION. It may change interfaces that you don't expect, but it is very easy to change back.

P.s. It will not work if you run Powershell with restricted language settings.

The script is invoked through the user command "BackgroundModes"

 ![image](https://resource.dopus.com/uploads/default/original/3X/c/7/c74126f6d8dcd4a8cf85100e47d144c7587ff960.jpeg)

 ![image](https://resource.dopus.com/uploads/default/original/3X/4/f/4fbf26569c4674f3e2dc263fa3e8bff22f3f6abc.jpeg)

```auto
// BackgroundModes.js
// J.Fourie
// Directory Opus Script Add-in (JScript)
//
// What is “canvas 5”:
// This script changes Windows system color index 5, also called COLOR_WINDOW.
// COLOR_WINDOW is the classic Win32 “window background” system color.
//
// What it affects:
// It can affect classic Windows controls that use the system window background:
// - Word, Excel, Outlook, and therefore the Preview Pane in DOPUS for these documents. 
// - older Win32 dialog backgrounds
// - some Directory Opus surfaces if they inherit COLOR_WINDOW
//
// It may not affect modern apps, browsers, Electron apps, UWP apps, or heavily
// themed programs because they often draw their own backgrounds.
//
// How it works:
// This script writes a temporary PowerShell helper, then PowerShell uses P/Invoke
// to call:
// - GetSysColor(5)
// - SetSysColors(5)
// - SendMessageTimeout(WM_SYSCOLORCHANGE)
//
// Safety model:
// The script changes only COLOR_WINDOW, not COLOR_WINDOWTEXT.
// Since text is assumed to remain black, all built-in colors are deliberately
// light but visibly non-white.
// SetSysColors affects the current Windows session; it is not written as a
// permanent Windows theme setting.
//
// Commands:
// BackgroundModes
// BackgroundModes MODE=Nord
// BackgroundModes MODE=Gruvbox
// BackgroundModes MODE=Normal
// BackgroundModes MODE=Custom HEX=D8F3DC
// BackgroundModes MODE=SetCustom

var VAR_ORIGINAL = "BackgroundModes.OriginalColorRef";
var VAR_CURRENT = "BackgroundModes.CurrentMode";
var VAR_CUSTOMHEX = "BackgroundModes.CustomHex";

var COLOR_WINDOW_INDEX = 5;
var DEFAULT_CUSTOM_HEX = "#D8F3DC";

// Minimum contrast ratio against black text.
var MIN_BLACK_TEXT_CONTRAST = 4.5;

function OnInit(initData)
{
    initData.name = "Background Modes";
    initData.version = "1.4";
    initData.desc = "Switch Windows COLOR_WINDOW background colors.";
    initData.default_enable = true;
    initData.min_version = "12.0";

    if (!initData.vars.Exists(VAR_CURRENT))
        initData.vars.Set(VAR_CURRENT, "Normal");

    if (!initData.vars.Exists(VAR_CUSTOMHEX)) {
        initData.vars.Set(VAR_CUSTOMHEX, DEFAULT_CUSTOM_HEX);
        initData.vars(VAR_CUSTOMHEX).persist = true;
    }

    var cmd = initData.AddCommand();
    cmd.name = "BackgroundModes";
    cmd.method = "OnBackgroundModes";
    cmd.desc = "Set or restore Windows COLOR_WINDOW background color.";
    cmd.label = "Background Modes";
    cmd.template = "MODE/O,HEX/O";
    cmd.icon = "prefs";
}

function OnShutdown(shutdownData)
{
    restoreOriginalQuietly("shutdown");
}

function OnBackgroundModes(data)
{
    try {
        ensureOriginalCaptured();

        var args = data.func.argsmap;
        var mode = "";
        var hex = "";

        if (args.exists("MODE"))
            mode = String(args("MODE"));

        if (args.exists("HEX"))
            hex = String(args("HEX"));

        if (mode == "") {
            mode = showModeMenu(data.func);
            if (mode == "")
                return;
        }

        mode = canonicalMode(mode);

        if (mode == "SetCustom") {
            setCustomHex(data.func, hex);
            return;
        }

        if (mode == "Custom" && hex != "") {
            setCustomHex(data.func, hex);
            return;
        }

        applyMode(mode);
    }
    catch (e) {
        showError(data.func, e);
    }
}

function modeDefs()
{
    var customHex = getCustomHex();

    return [
        { name:"Normal", label:"Normal (restore defaults)", color:null },

        // Practical distinct light backgrounds.
        { name:"SoftGreen", label:"Soft Green (#D8F3DC)", color:colorRef("#D8F3DC") },
        { name:"Mint", label:"Mint (#CDEAC0)", color:colorRef("#CDEAC0") },
        { name:"Sage", label:"Sage (#DDE5B6)", color:colorRef("#DDE5B6") },
        { name:"Sand", label:"Sand (#EAD7BB)", color:colorRef("#EAD7BB") },
        { name:"Sepia", label:"Sepia (#E8DCC8)", color:colorRef("#E8DCC8") },
        { name:"Parchment", label:"Parchment (#F5DEB3)", color:colorRef("#F5DEB3") },
        { name:"Peach", label:"Peach (#FFD6BA)", color:colorRef("#FFD6BA") },
        { name:"Apricot", label:"Apricot (#F4C2A1)", color:colorRef("#F4C2A1") },
        { name:"Blush", label:"Blush (#F2C6C2)", color:colorRef("#F2C6C2") },
        { name:"Lavender", label:"Lavender (#D8CCF1)", color:colorRef("#D8CCF1") },
        { name:"Sky", label:"Sky (#C7DDF2)", color:colorRef("#C7DDF2") },
        { name:"PowderBlue", label:"Powder Blue (#BFD7EA)", color:colorRef("#BFD7EA") },

        // Popular scheme-inspired, but adjusted to be visibly non-white.
        { name:"Nord", label:"Nord Frost Light (#C8D9E8)", color:colorRef("#C8D9E8") },
        { name:"Dracula", label:"Dracula Soft Pink (#E9C7D9)", color:colorRef("#E9C7D9") },
        { name:"Gruvbox", label:"Gruvbox Warm Light (#EBDBB2)", color:colorRef("#EBDBB2") },
        { name:"Solarized", label:"Solarized Cream (#EEE8D5)", color:colorRef("#EEE8D5") },
        { name:"Catppuccin", label:"Catppuccin Peach Light (#F2CDCD)", color:colorRef("#F2CDCD") },
        { name:"RosePine", label:"Rosé Pine Dawn Iris (#DFD7F4)", color:colorRef("#DFD7F4") },
        { name:"Kanagawa", label:"Kanagawa Lotus (#E6C384)", color:colorRef("#E6C384") },

        { name:"Custom", label:"Custom (" + customHex + ")", color:colorRef(customHex) }
    ];
}

function applyMode(mode)
{
    if (mode == "Normal") {
        restoreOriginal();
        setCurrentMode("Normal");
        return;
    }

    var defs = modeDefs();

    for (var i = 0; i < defs.length; i++) {
        if (defs[i].name == mode) {
            setColorRef(defs[i].color);
            setCurrentMode(mode);
            return;
        }
    }

    throw "Unknown mode: " + mode;
}

function restoreOriginal()
{
    if (!Script.vars.Exists(VAR_ORIGINAL))
        throw "No original COLOR_WINDOW value has been captured yet.";

    setColorRef(Number(Script.vars.Get(VAR_ORIGINAL)));
}

function restoreOriginalQuietly(context)
{
    try {
        if (Script.vars.Exists(VAR_ORIGINAL))
            setColorRef(Number(Script.vars.Get(VAR_ORIGINAL)));
    }
    catch (e) {
        DOpus.Output("Background Modes " + context + " restore failed: " + e);
    }
}

function ensureOriginalCaptured()
{
    if (!Script.vars.Exists(VAR_ORIGINAL))
        Script.vars.Set(VAR_ORIGINAL, getColorRef());
}

function setCurrentMode(mode)
{
    Script.vars.Set(VAR_CURRENT, mode);
}

function getCurrentMode()
{
    if (Script.vars.Exists(VAR_CURRENT))
        return String(Script.vars.Get(VAR_CURRENT));

    return "Normal";
}

function showModeMenu(func)
{
    var defs = modeDefs();
    var current = getCurrentMode();
    var choices = [];
    var flags = [];

    for (var i = 0; i < defs.length; i++) {
        choices.push(defs[i].label);
        flags.push(defs[i].name == current ? 4 : 0);
    }

    choices.push("-");
    flags.push(0);

    choices.push("Set Custom...");
    flags.push(0);

    var dlg = func.Dlg();
    dlg.title = "Background Modes";
    dlg.choices = choices;
    dlg.menu = flags;

    var result = dlg.Show();

    if (result == 0)
        return "";

    if (result == choices.length)
        return "SetCustom";

    return defs[result - 1].name;
}

function setCustomHex(func, suppliedHex)
{
    var hex = suppliedHex;

    if (hex == "") {
        var dlg = func.Dlg();
        dlg.title = "Custom COLOR_WINDOW";
        dlg.message = "Enter a light RGB hex color suitable for black text, e.g. #D8F3DC";
        dlg.buttons = "OK|Cancel";
        dlg.max = 7;
        dlg.defvalue = getCustomHex();
        dlg.select = true;

        if (dlg.Show() != 1)
            return;

        hex = dlg.input;
    }

    saveCustomHex(hex);
    applyMode("Custom");
}

function saveCustomHex(hex)
{
    hex = normalizeHex(hex);
    assertReadableWithBlackText(hex);

    Script.vars.Set(VAR_CUSTOMHEX, hex);
    Script.vars(VAR_CUSTOMHEX).persist = true;
}

function getCustomHex()
{
    if (Script.vars.Exists(VAR_CUSTOMHEX)) {
        try {
            return normalizeHex(String(Script.vars.Get(VAR_CUSTOMHEX)));
        }
        catch (e) {
            return DEFAULT_CUSTOM_HEX;
        }
    }

    return DEFAULT_CUSTOM_HEX;
}

function canonicalMode(mode)
{
    var key = String(mode).replace(/^\s+|\s+$/g, "").toLowerCase();

    if (key == "setcustom" || key == "set custom" || key == "setcustomcolor")
        return "SetCustom";

    var aliases = {
        // Previous basic names.
        "morning": "SoftGreen",
        "day": "Sage",

        // Direct aliases.
        "softgreen": "SoftGreen",
        "soft green": "SoftGreen",
        "powderblue": "PowderBlue",
        "powder blue": "PowderBlue",

        // Popular scheme aliases.
        "nordfrost": "Nord",
        "nord frost": "Nord",

        "draculalight": "Dracula",
        "dracula light": "Dracula",

        "gruvboxlight": "Gruvbox",
        "gruvbox light": "Gruvbox",

        "solarizedlight": "Solarized",
        "solarized light": "Solarized",

        "catppuccinlatte": "Catppuccin",
        "catppuccin latte": "Catppuccin",

        "rosepine": "RosePine",
        "rose pine": "RosePine",
        "rosepinedawn": "RosePine",
        "rose pine dawn": "RosePine",
        "rosé pine": "RosePine",
        "rosé pine dawn": "RosePine",

        "kanagawalotus": "Kanagawa",
        "kanagawa lotus": "Kanagawa",

        // Old dark/night names redirected to light readable colors.
        "night": "Sky",
        "evening": "Sepia",
        "midnight": "PowderBlue",
        "twilight": "Lavender",
        "nighttime": "Sand",
        "night time": "Sand",

        // Removed/old names redirected, but not shown in the menu.
        "burlywood": "Sand",
        "daytime": "Parchment",
        "day time": "Parchment",
        "careeye": "Sepia",
        "care eye": "Sepia"
    };

    if (aliases[key])
        return aliases[key];

    var defs = modeDefs();

    for (var i = 0; i < defs.length; i++) {
        if (defs[i].name.toLowerCase() == key)
            return defs[i].name;
    }

    throw "Unknown mode: " + mode;
}

function normalizeHex(hex)
{
    hex = String(hex).replace(/^\s+|\s+$/g, "");

    if (hex.charAt(0) == "#")
        hex = hex.substr(1);

    if (!/^[0-9a-fA-F]{6}$/.test(hex))
        throw "Invalid color. Use #RRGGBB, e.g. #D8F3DC.";

    return "#" + hex.toUpperCase();
}

function colorRef(hex)
{
    return rgbHexToBgr(hex);
}

function rgbHexToBgr(hex)
{
    hex = normalizeHex(hex).substr(1);

    var r = parseInt(hex.substr(0, 2), 16);
    var g = parseInt(hex.substr(2, 2), 16);
    var b = parseInt(hex.substr(4, 2), 16);

    return (b << 16) | (g << 8) | r;
}

function assertReadableWithBlackText(hex)
{
    var contrast = contrastAgainstBlack(hex);

    if (contrast < MIN_BLACK_TEXT_CONTRAST) {
        throw "Custom color " + normalizeHex(hex) +
              " is too dark for black text. Use a lighter color.";
    }
}

function contrastAgainstBlack(hex)
{
    var luminance = relativeLuminance(hex);

    // Black luminance is 0.
    return (luminance + 0.05) / 0.05;
}

function relativeLuminance(hex)
{
    hex = normalizeHex(hex).substr(1);

    var r = parseInt(hex.substr(0, 2), 16);
    var g = parseInt(hex.substr(2, 2), 16);
    var b = parseInt(hex.substr(4, 2), 16);

    var rl = linearRgbChannel(r);
    var gl = linearRgbChannel(g);
    var bl = linearRgbChannel(b);

    return (0.2126 * rl) + (0.7152 * gl) + (0.0722 * bl);
}

function linearRgbChannel(value)
{
    var c = value / 255;

    if (c <= 0.03928)
        return c / 12.92;

    return Math.pow((c + 0.055) / 1.055, 2.4);
}

function getColorRef()
{
    var out = runPowerShellHelper("get", 0);

    out = String(out).replace(/^\s+|\s+$/g, "");

    if (!/^\d+$/.test(out))
        throw "Could not read COLOR_WINDOW. PowerShell returned: " + out;

    return Number(out);
}

function setColorRef(colorRef)
{
    runPowerShellHelper("set", Number(colorRef));
}

function runPowerShellHelper(action, colorRef)
{
    var shell = new ActiveXObject("WScript.Shell");
    var psPath = "";
    var stdout = "";
    var stderr = "";
    var exitCode = 0;

    try {
        psPath = writePowerShellHelper(shell);

        var cmd = "powershell.exe -NoProfile -ExecutionPolicy Bypass -NonInteractive" +
                  " -File " + quoteArg(psPath) +
                  " -Action " + quoteArg(action) +
                  " -Color " + String(Number(colorRef));

        var exec = shell.Exec(cmd);

        while (exec.Status == 0)
            DOpus.Delay(10);

        stdout = exec.StdOut.ReadAll();
        stderr = exec.StdErr.ReadAll();
        exitCode = exec.ExitCode;

        if (exitCode != 0)
            throw "PowerShell helper failed: " + stderr;

        return stdout;
    }
    finally {
        if (psPath != "")
            deleteFileQuietly(psPath);
    }
}

function writePowerShellHelper(shell)
{
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var temp = shell.ExpandEnvironmentStrings("%TEMP%");
    var name = "DOpus_BackgroundModes_" + String(new Date().getTime()) + "_" + String(Math.floor(Math.random() * 1000000)) + ".ps1";
    var path = temp + "\\" + name;

    var ps =
        "param(\r\n" +
        " [Parameter(Mandatory=$true)][ValidateSet('get','set')][string]$Action,\r\n" +
        " [UInt32]$Color = 0\r\n" +
        ")\r\n" +
        "$ErrorActionPreference = 'Stop'\r\n" +
        "$src = @'\r\n" +
        "using System;\r\n" +
        "using System.Runtime.InteropServices;\r\n" +
        "public static class DOpusBackgroundModesWin32 {\r\n" +
        " [DllImport(\"user32.dll\")]\r\n" +
        " public static extern uint GetSysColor(int nIndex);\r\n" +
        " [DllImport(\"user32.dll\", SetLastError=true)]\r\n" +
        " public static extern bool SetSysColors(int cElements, int[] lpaElements, uint[] lpaRgbValues);\r\n" +
        " [DllImport(\"user32.dll\", SetLastError=true, CharSet=CharSet.Auto)]\r\n" +
        " public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);\r\n" +
        "}\r\n" +
        "'@\r\n" +
        "Add-Type -TypeDefinition $src\r\n" +
        "$idx = " + String(COLOR_WINDOW_INDEX) + " # COLOR_WINDOW\r\n" +
        "if ($Action -eq 'get') {\r\n" +
        " [DOpusBackgroundModesWin32]::GetSysColor($idx)\r\n" +
        " exit 0\r\n" +
        "}\r\n" +
        "$ok = [DOpusBackgroundModesWin32]::SetSysColors(1, [int[]]@($idx), [uint32[]]@($Color))\r\n" +
        "if (-not $ok) { throw 'SetSysColors failed.' }\r\n" +
        "$result = [UIntPtr]::Zero\r\n" +
        "[DOpusBackgroundModesWin32]::SendMessageTimeout([IntPtr]0xffff, [uint32]0x0015, [UIntPtr]::Zero, [IntPtr]::Zero, [uint32]0x0002, [uint32]250, [ref]$result) | Out-Null\r\n";

    var file = fso.CreateTextFile(path, true, false);
    file.Write(ps);
    file.Close();

    return path;
}

function deleteFileQuietly(path)
{
    try {
        var fso = new ActiveXObject("Scripting.FileSystemObject");

        if (fso.FileExists(path))
            fso.DeleteFile(path, true);
    }
    catch (ignored) {
    }
}

function quoteArg(s)
{
    return '"' + String(s) + '"';
}

function showError(func, e)
{
    var message = String(e);

    DOpus.Output("Background Modes error: " + message);

    try {
        var dlg = func.Dlg();
        dlg.title = "Background Modes";
        dlg.message = message;
        dlg.buttons = "OK";
        dlg.icon = "error";
        dlg.Show();
    }
    catch (ignored) {
    }
}

```

---

<div class="post-metadata">

**Author:** ![Leo](https://resource.dopus.com/user_avatar/resource.dopus.com/leo/32/69732_2.png) [@Leo](https://resource.dopus.com/u/Leo)\
**Post date:** [May 7, 2026, 11:54am UTC](https://resource.dopus.com/t/canvas5-theme/59351/2 "2026-05-07T11:54:48Z")

</div>

> [@jfour18](#):
>
> ```auto
> // How it works:
> // This script writes a temporary PowerShell helper, then PowerShell uses P/Invoke
> // to call:
> // - GetSysColor(5)
> // - SetSysColors(5)
> // - SendMessageTimeout(WM_SYSCOLORCHANGE)
> 
> ```

Doing that doesn't make sense to use in Opus, at least not in Opus 13. We already let you change the system colors via **Preferences / Colors and Fonts / Windows Colors**.

Color 5 / COLOR\_WINDOW is under **Window contents, Background**.

It will already be a dark colour by default if you're using Opus in dark mode.

---

<div class="post-metadata">

**Author:** ![jfour18](https://resource.dopus.com/user_avatar/resource.dopus.com/jfour18/32/66032_2.png) [@jfour18](https://resource.dopus.com/u/jfour18)\
**Post date:** [May 7, 2026, 12:12pm UTC](https://resource.dopus.com/t/canvas5-theme/59351/3 "2026-05-07T12:12:01Z")

</div>

Very nice! I did not know that 😄

It's main benefit for me is changing the default background in Microsoft Word, though this can be done with macros as well.

---

<div class="post-metadata">

**Author:** ![jfour18](https://resource.dopus.com/user_avatar/resource.dopus.com/jfour18/32/66032_2.png) [@jfour18](https://resource.dopus.com/u/jfour18)\
**Post date:** [August 24, 2026, 7:08am UTC](https://resource.dopus.com/t/canvas5-theme/59351/4 "2026-08-24T07:08:17Z")

</div>

Hi Leo,

Is 'Color\_Window' in Preferences intended to change the white canvas of the PDF/word doc itself when it is rendered in the preview pane (or quickshow viewer), or does it only change the color of the canvas surrounding the document? I ask because the actual color of the previewed document remains white for me.

---

<div class="post-metadata">

**Author:** ![Leo](https://resource.dopus.com/user_avatar/resource.dopus.com/leo/32/69732_2.png) [@Leo](https://resource.dopus.com/u/Leo)\
**Post date:** [August 24, 2026, 7:42am UTC](https://resource.dopus.com/t/canvas5-theme/59351/5 "2026-08-24T07:42:43Z")

</div>

It’s up to the viewers which colors they use.

---

<div class="post-metadata">

**Author:** ![jfour18](https://resource.dopus.com/user_avatar/resource.dopus.com/jfour18/32/66032_2.png) [@jfour18](https://resource.dopus.com/u/jfour18)\
**Post date:** [August 24, 2026, 11:54am UTC](https://resource.dopus.com/t/canvas5-theme/59351/6 "2026-08-24T11:54:24Z")

</div>

It would be awesome if these panes could be changed to dark mode natively, as they conflict quite strongly with dark themes, especially late at night.

I use the following AHK script which changes both the preview pane and the quickshow pane to dark mode for PDF files and word documents, respectively.

 ![Dark preview](https://resource.dopus.com/uploads/default/original/3X/1/7/17e19a5d336673bada959b24241ec6a16eb0156a.jpeg)

```auto
#Requires AutoHotkey v2.0
#SingleInstance Force

global ENABLE_PDF_DARK_MODE := true
global ENABLE_WORD_DARK_MODE := true

global DOCUMENT_BACKGROUND := "1E1E1E"
global DOCUMENT_TEXT := "D9CBB9"

global UPDATE_INTERVAL := 30

if A_Is64bitOS && A_PtrSize != 8 {
    MsgBox "This script requires 64-bit AutoHotkey v2."
    ExitApp
}

global hMagDll := DllCall(
    "Kernel32\LoadLibraryW",
    "Str", "Magnification.dll",
    "Ptr"
)

if !hMagDll {
    MsgBox "Could not load Magnification.dll."
    ExitApp
}

if !DllCall("Magnification\MagInitialize", "Int") {
    MsgBox "MagInitialize() failed."
    ExitApp
}

global magInitialized := true
OnExit(Cleanup)

global host := Gui(
    "+ToolWindow -Caption -DPIScale"
    " +E0x00080000"
    " +E0x00000020"
    " +E0x08000000"
)

host.BackColor := "000000"
host.Show("NA x0 y0 w1 h1")

if !DllCall(
    "User32\SetLayeredWindowAttributes",
    "Ptr", host.Hwnd,
    "UInt", 0,
    "UChar", 255,
    "UInt", 0x2,
    "Int"
) {
    MsgBox "SetLayeredWindowAttributes() failed."
    ExitApp
}

hInstance := DllCall(
    "User32\GetWindowLongPtrW",
    "Ptr", host.Hwnd,
    "Int", -6,
    "Ptr"
)

global hMag := DllCall(
    "User32\CreateWindowExW",
    "UInt", 0,
    "Str", "Magnifier",
    "Str", "",
    "UInt", 0x50000000,
    "Int", 0,
    "Int", 0,
    "Int", 1,
    "Int", 1,
    "Ptr", host.Hwnd,
    "Ptr", 0,
    "Ptr", hInstance,
    "Ptr", 0,
    "Ptr"
)

if !hMag {
    MsgBox "Could not create Magnifier control.`nWindows error: " A_LastError
    ExitApp
}

transform := Buffer(9 * 4, 0)

NumPut("Float", 1.0, transform, 0)
NumPut("Float", 1.0, transform, 16)
NumPut("Float", 1.0, transform, 32)

if !DllCall(
    "Magnification\MagSetWindowTransform",
    "Ptr", hMag,
    "Ptr", transform.Ptr,
    "Int"
) {
    MsgBox "MagSetWindowTransform() failed."
    ExitApp
}

filter := Buffer(A_PtrSize, 0)
NumPut("Ptr", host.Hwnd, filter, 0)

if !DllCall(
    "Magnification\MagSetWindowFilterList",
    "Ptr", hMag,
    "UInt", 0,
    "Int", 1,
    "Ptr", filter.Ptr,
    "Int"
) {
    MsgBox "MagSetWindowFilterList() failed."
    ExitApp
}

global documentMatrix := CreateDocumentMatrix()
global colourEffect := BuildColourEffect(documentMatrix)
global lastTarget := 0

SetTimer(UpdateDocumentMode, UPDATE_INTERVAL)

HexToRGB(hex) {
    hex := StrReplace(hex, "#", "")

    return {
        r: Integer("0x" SubStr(hex, 1, 2)) / 255.0,
        g: Integer("0x" SubStr(hex, 3, 2)) / 255.0,
        b: Integer("0x" SubStr(hex, 5, 2)) / 255.0
    }
}

CreateDocumentMatrix() {
    global DOCUMENT_BACKGROUND, DOCUMENT_TEXT

    bg := HexToRGB(DOCUMENT_BACKGROUND)
    text := HexToRGB(DOCUMENT_TEXT)

    return [
        bg.r - text.r, 0.0, 0.0, 0.0, 0.0,
        0.0, bg.g - text.g, 0.0, 0.0, 0.0,
        0.0, 0.0, bg.b - text.b, 0.0, 0.0,
        0.0, 0.0, 0.0, 1.0, 0.0,
        text.r, text.g, text.b, 0.0, 1.0
    ]
}

BuildColourEffect(matrix) {
    effect := Buffer(25 * 4, 0)

    Loop 25
        NumPut("Float", matrix[A_Index], effect, (A_Index - 1) * 4)

    return effect
}

GetWindowClass(hwnd) {
    buf := Buffer(1024, 0)

    len := DllCall(
        "User32\GetClassNameW",
        "Ptr", hwnd,
        "Ptr", buf.Ptr,
        "Int", 512,
        "Int"
    )

    return len ? StrGet(buf, len, "UTF-16") : ""
}

FindDescendantByClass(parentHwnd, wantedClass) {
    child := 0

    Loop {
        child := DllCall(
            "User32\FindWindowExW",
            "Ptr", parentHwnd,
            "Ptr", child,
            "Ptr", 0,
            "Ptr", 0,
            "Ptr"
        )

        if !child
            break

        if GetWindowClass(child) = wantedClass
            return child

        found := FindDescendantByClass(child, wantedClass)

        if found
            return found
    }

    return 0
}

FindDescendantByClassPrefix(parentHwnd, prefix) {
    child := 0

    Loop {
        child := DllCall(
            "User32\FindWindowExW",
            "Ptr", parentHwnd,
            "Ptr", child,
            "Ptr", 0,
            "Ptr", 0,
            "Ptr"
        )

        if !child
            break

        class := GetWindowClass(child)

        if SubStr(class, 1, StrLen(prefix)) = prefix
            return child

        found := FindDescendantByClassPrefix(child, prefix)

        if found
            return found
    }

    return 0
}

GetDocumentInfo(hwnd) {
    global ENABLE_PDF_DARK_MODE, ENABLE_WORD_DARK_MODE

    try class := WinGetClass("ahk_id " hwnd)
    catch
        return false

    if class != "dopus.viewpicframe" && class != "dopus.lister"
        return false

    if ENABLE_WORD_DARK_MODE {
        render := FindDescendantByClassPrefix(hwnd, "_WwG")

        if render {
            return {
                hwnd: hwnd,
                render: render,
                mode: "word"
            }
        }
    }

    if ENABLE_PDF_DARK_MODE {
        render := FindDescendantByClass(hwnd, "Intermediate D3D Window")

        if render {
            return {
                hwnd: hwnd,
                render: render,
                mode: "pdf"
            }
        }
    }

    return false
}

FindDocumentTarget() {
    global lastTarget

    active := WinActive("ahk_exe dopus.exe")

    if active {
        info := GetDocumentInfo(active)

        if info
            return info
    }

    if lastTarget && WinExist("ahk_id " lastTarget) {
        info := GetDocumentInfo(lastTarget)

        if info
            return info
    }

    for hwnd in WinGetList("ahk_exe dopus.exe") {
        info := GetDocumentInfo(hwnd)

        if info
            return info
    }

    return false
}

UpdateDocumentMode() {
    global host, hMag, colourEffect, lastTarget

    info := FindDocumentTarget()

    if !info {
        host.Hide()
        lastTarget := 0
        return
    }

    target := info.hwnd
    render := info.render

    try {
        if WinGetMinMax("ahk_id " target) = -1 {
            host.Hide()
            return
        }
    } catch {
        host.Hide()
        return
    }

    rect := Buffer(16, 0)

    if !DllCall(
        "User32\GetWindowRect",
        "Ptr", render,
        "Ptr", rect.Ptr,
        "Int"
    ) {
        host.Hide()
        return
    }

    left := NumGet(rect, 0, "Int")
    top := NumGet(rect, 4, "Int")
    right := NumGet(rect, 8, "Int")
    bottom := NumGet(rect, 12, "Int")

    width := right - left
    height := bottom - top

    if width <= 0 || height <= 0 {
        host.Hide()
        return
    }

    if !DllCall(
        "Magnification\MagSetColorEffect",
        "Ptr", hMag,
        "Ptr", colourEffect.Ptr,
        "Int"
    )
        return

    if target != lastTarget {
        DllCall(
            "User32\SetWindowLongPtrW",
            "Ptr", host.Hwnd,
            "Int", -8,
            "Ptr", target,
            "Ptr"
        )

        lastTarget := target
    }

    host.Show(
        "NA"
        " x" left
        " y" top
        " w" width
        " h" height
    )

    DllCall(
        "User32\MoveWindow",
        "Ptr", hMag,
        "Int", 0,
        "Int", 0,
        "Int", width,
        "Int", height,
        "Int", 1
    )

    source := Buffer(16, 0)

    NumPut("Int", left, source, 0)
    NumPut("Int", top, source, 4)
    NumPut("Int", right, source, 8)
    NumPut("Int", bottom, source, 12)

    DllCall(
        "Magnification\MagSetWindowSource",
        "Ptr", hMag,
        "Ptr", source.Ptr,
        "Int"
    )
}

^!F12:: {
    active := WinActive("ahk_exe dopus.exe")

    if !active {
        MsgBox "No active DOpus window."
        return
    }

    title := WinGetTitle("ahk_id " active)
    class := WinGetClass("ahk_id " active)

    word := FindDescendantByClassPrefix(active, "_WwG")
    pdf := FindDescendantByClass(active, "Intermediate D3D Window")

    wordClass := word ? GetWindowClass(word) : "NOT FOUND"
    pdfClass := pdf ? GetWindowClass(pdf) : "NOT FOUND"

    MsgBox(
        "Title: " title
        "`nClass: " class
        "`nWord renderer: " wordClass
        "`nPDF renderer: " pdfClass
    )
}

Cleanup(*) {
    global host, hMagDll, magInitialized

    try SetTimer(UpdateDocumentMode, 0)
    try host.Destroy()

    if magInitialized {
        DllCall("Magnification\MagUninitialize")
        magInitialized := false
    }

    if hMagDll {
        DllCall("Kernel32\FreeLibrary", "Ptr", hMagDll)
        hMagDll := 0
    }
}

```

---

<div class="post-metadata">

**Author:** ![Leo](https://resource.dopus.com/user_avatar/resource.dopus.com/leo/32/69732_2.png) [@Leo](https://resource.dopus.com/u/Leo)\
**Post date:** [August 24, 2026, 7:17pm UTC](https://resource.dopus.com/t/canvas5-theme/59351/7 "2026-08-24T19:17:37Z")

</div>

What happens if there's a photo in the document? Won't applying a magnifier filter like that mess up the image, or potentially cause other problems?

---

<div class="post-metadata">

**Author:** ![jfour18](https://resource.dopus.com/user_avatar/resource.dopus.com/jfour18/32/66032_2.png) [@jfour18](https://resource.dopus.com/u/jfour18)\
**Post date:** [August 25, 2026, 12:38am UTC](https://resource.dopus.com/t/canvas5-theme/59351/8 "2026-08-25T00:38:26Z")

</div>

None that I have encountered. Images render fine. In the current script they are merely inverted which is not unpleasant. In a previous version I applied a light gray or sepia overlay which eliminated the inversion problem.

 ![image](https://resource.dopus.com/uploads/default/original/3X/7/2/72342a42183e679a5f7120f7d00cc5afefe77003.jpeg)

---

<div class="post-metadata">

**Author:** ![Leo](https://resource.dopus.com/user_avatar/resource.dopus.com/leo/32/69732_2.png) [@Leo](https://resource.dopus.com/u/Leo)\
**Post date:** [August 25, 2026, 8:28am UTC](https://resource.dopus.com/t/canvas5-theme/59351/9 "2026-08-25T08:28:29Z")

</div>

> [@jfour18](#):
>
> Images render fine. In the current script they are merely inverted which is not unpleasant.

I don’t think inverting all images is them rendering fine. We would get so many complaints and questions if we did that. 🙂
